Пример #1
0
    def test_config(self):
        """test hashing interface

        this function is run against both the actual django code, to
        verify the assumptions of the unittests are correct;
        and run against the passlib extension, to verify it matches
        those assumptions.
        """
        patched, config = self.patched, self.config
        # this tests the following methods:
        #   User.set_password()
        #   User.check_password()
        #   make_password() -- 1.4 only
        #   check_password()
        #   identify_hasher()
        #   User.has_usable_password()
        #   User.set_unusable_password()
        # XXX: this take a while to run. what could be trimmed?

        #  TODO: get_hasher()

        #=======================================================
        # setup helpers & imports
        #=======================================================
        ctx = self.context
        setter = create_mock_setter()
        PASS1 = "toomanysecrets"
        WRONG1 = "letmein"

        if has_django14:
            from passlib.ext.django.utils import hasher_to_passlib_name, passlib_to_hasher_name
            from django.contrib.auth.hashers import check_password, make_password, is_password_usable
            if patched:
                from django.contrib.auth.hashers import identify_hasher
        else:
            from django.contrib.auth.models import check_password

        #=======================================================
        # make sure extension is configured correctly
        #=======================================================
        if patched:
            # contexts should match
            from passlib.ext.django.models import password_context
            self.assertEqual(password_context.to_dict(resolve=True),
                             ctx.to_dict(resolve=True))

            # should have patched both places
            if has_django14:
                from django.contrib.auth.models import check_password as check_password2
                self.assertIs(check_password2, check_password)

        #=======================================================
        # default algorithm
        #=======================================================
        # User.set_password() should use default alg
        user = FakeUser()
        user.set_password(PASS1)
        self.assertTrue(ctx.handler().verify(PASS1, user.password))
        self.assert_valid_password(user)

        # User.check_password() - n/a

        # make_password() should use default alg
        if has_django14:
            hash = make_password(PASS1)
            self.assertTrue(ctx.handler().verify(PASS1, hash))

        # check_password() - n/a

        #=======================================================
        # empty password behavior
        #=======================================================
        if has_django14:
            # NOTE: django 1.4 treats empty password as invalid

            # User.set_password() should set unusable flag
            user = FakeUser()
            user.set_password('')
            self.assert_unusable_password(user)

            # User.check_password() should never return True
            user = FakeUser()
            user.password = hash = ctx.encrypt("")
            self.assertFalse(user.check_password(""))
            self.assert_valid_password(user, hash)

            # make_password() should reject empty passwords
            self.assertEqual(make_password(""), "!")

            # check_password() should never return True
            self.assertFalse(check_password("", hash))

        else:
            # User.set_password() should use default alg
            user = FakeUser()
            user.set_password('')
            hash = user.password
            self.assertTrue(ctx.handler().verify('', hash))
            self.assert_valid_password(user, hash)

            # User.check_password() should return True
            self.assertTrue(user.check_password(""))
            self.assert_valid_password(user, hash)

            # no make_password()

            # check_password() should return True
            self.assertTrue(check_password("", hash))

        #=======================================================
        # 'unusable flag' behavior
        #=======================================================
        if has_django1 or patched:

            # sanity check via user.set_unusable_password()
            user = FakeUser()
            user.set_unusable_password()
            self.assert_unusable_password(user)

            # ensure User.set_password() sets flag
            user = FakeUser()
            user.set_password(None)
            self.assert_unusable_password(user)

            # User.check_password() should always fail
            self.assertFalse(user.check_password(None))
            self.assertFalse(user.check_password(''))
            self.assertFalse(user.check_password(PASS1))
            self.assertFalse(user.check_password(WRONG1))
            self.assert_unusable_password(user)

            # make_password() should also set flag
            if has_django14:
                self.assertEqual(make_password(None), "!")

            # check_password() should return False (didn't handle disabled under 1.3)
            if has_django14 or patched:
                self.assertFalse(check_password(PASS1, '!'))

            # identify_hasher() and is_password_usable() should reject it
            if has_django14:
                self.assertFalse(is_password_usable(user.password))
            if has_django14 and patched:
                self.assertRaises(ValueError, identify_hasher, user.password)

        #=======================================================
        # hash=None
        #=======================================================
        # User.set_password() - n/a

        # User.check_password() - returns False
        user = FakeUser()
        user.password = None
        if has_django14 or patched:
            self.assertFalse(user.check_password(PASS1))
        else:
            self.assertRaises(TypeError, user.check_password, PASS1)
        if has_django1 or patched:
            self.assertFalse(user.has_usable_password())

        # make_password() - n/a

        # check_password() - error
        if has_django14 or patched:
            self.assertFalse(check_password(PASS1, None))
        else:
            self.assertRaises(AttributeError, check_password, PASS1, None)

        # identify_hasher() - error
        if has_django14 and patched:
            self.assertRaises(TypeError, identify_hasher, None)

        #=======================================================
        # invalid hash values
        #=======================================================
        for hash in ("", "$789$foo"):
            # User.set_password() - n/a

            # User.check_password() - blank hash causes error
            user = FakeUser()
            user.password = hash
            if has_django14 or patched or hash:
                self.assertRaises(ValueError, user.check_password, PASS1)
            else:
                # django 1.3 returns False for empty hashes
                self.assertFalse(user.check_password(PASS1))
            self.assert_valid_password(
                user, hash)  # '' counts as valid for some reason

            # make_password() - n/a

            # check_password() - error
            self.assertRaises(ValueError, check_password, PASS1, hash)

            # identify_hasher() - error
            if has_django14 and patched:
                self.assertRaises(ValueError, identify_hasher, hash)

        #=======================================================
        # run through all the schemes in the context,
        # testing various bits of per-scheme behavior.
        #=======================================================
        for scheme in ctx.schemes():
            #-------------------------------------------------------
            # setup constants & imports, pick a sample secret/hash combo
            #-------------------------------------------------------
            handler = ctx.handler(scheme)
            deprecated = ctx._is_deprecated_scheme(scheme)
            assert not deprecated or scheme != ctx.default_scheme()
            try:
                testcase = get_handler_case(scheme)
            except exc.MissingBackendError:
                assert scheme == "bcrypt"
                continue
            assert testcase.handler is handler
            if testcase.is_disabled_handler:
                continue
            if not has_active_backend(handler):
                assert scheme == "django_bcrypt"
                continue
            while True:
                secret, hash = testcase('setUp').get_sample_hash()
                if secret:  # don't select blank passwords, special under django
                    break
            other = 'letmein'

            # User.set_password() - n/a

            #-------------------------------------------------------
            # User.check_password()+migration against known hash
            #-------------------------------------------------------
            user = FakeUser()
            user.password = hash

            # check against invalid password
            if has_django1 or patched:
                self.assertFalse(user.check_password(None))
            else:
                self.assertRaises(TypeError, user.check_password, None)
            ##self.assertFalse(user.check_password(''))
            self.assertFalse(user.check_password(other))
            self.assert_valid_password(user, hash)

            # check against valid password
            if has_django0 and isinstance(secret, unicode):
                secret = secret.encode("utf-8")
            self.assertTrue(user.check_password(secret))

            # check if it upgraded the hash
            needs_update = deprecated
            if needs_update:
                self.assertFalse(handler.identify(user.password))
                self.assertTrue(ctx.handler().verify(secret, user.password))
                self.assert_valid_password(user, saved=user.password)
            else:
                self.assert_valid_password(user, hash)

            # don't need to check rest for most deployments
            if TEST_MODE(max="default"):
                continue

            #-------------------------------------------------------
            # make_password() correctly selects algorithm
            #-------------------------------------------------------
            if has_django14:
                hash2 = make_password(secret,
                                      hasher=passlib_to_hasher_name(scheme))
                self.assertTrue(handler.verify(secret, hash2))

            #-------------------------------------------------------
            # check_password()+setter against known hash
            #-------------------------------------------------------
            if has_django14 or patched:
                # should call setter only if it needs_update
                self.assertTrue(check_password(secret, hash, setter=setter))
                self.assertEqual(setter.popstate(),
                                 [secret] if needs_update else [])

                # should not call setter
                self.assertFalse(check_password(other, hash, setter=setter))
                self.assertEqual(setter.popstate(), [])

                ### check preferred kwd is ignored (django 1.4 feature we don't support)
                ##self.assertTrue(check_password(secret, hash, setter=setter, preferred='fooey'))
                ##self.assertEqual(setter.popstate(), [secret])

            elif patched or scheme != "hex_md5":
                # django 1.3 never called check_password() for hex_md5
                self.assertTrue(check_password(secret, hash))
                self.assertFalse(check_password(other, hash))

            # TODO: get_hasher()

            #-------------------------------------------------------
            # identify_hasher() recognizes known hash
            #-------------------------------------------------------
            if has_django14 and patched:
                self.assertTrue(is_password_usable(hash))
                name = hasher_to_passlib_name(identify_hasher(hash).algorithm)
                self.assertEqual(name, scheme)
Пример #2
0
    def test_config(self):
        """test hashing interface

        this function is run against both the actual django code, to
        verify the assumptions of the unittests are correct;
        and run against the passlib extension, to verify it matches
        those assumptions.
        """
        patched, config = self.patched, self.config
        # this tests the following methods:
        #   User.set_password()
        #   User.check_password()
        #   make_password() -- 1.4 only
        #   check_password()
        #   identify_hasher()
        #   User.has_usable_password()
        #   User.set_unusable_password()
        # XXX: this take a while to run. what could be trimmed?

        #  TODO: get_hasher()

        #=======================================================
        # setup helpers & imports
        #=======================================================
        ctx = self.context
        setter = create_mock_setter()
        PASS1 = "toomanysecrets"
        WRONG1 = "letmein"

        has_identify_hasher = False
        from passlib.ext.django.utils import hasher_to_passlib_name, passlib_to_hasher_name
        from django.contrib.auth.hashers import check_password, make_password, is_password_usable
        if patched or DJANGO_VERSION >= (1,5):
            # identify_hasher()
            #   django 1.4 -- not present
            #   django 1.5 -- present (added in django ticket 18184)
            #   passlib integration -- present even under 1.4
            from django.contrib.auth.hashers import identify_hasher
            has_identify_hasher = True

        #=======================================================
        # make sure extension is configured correctly
        #=======================================================
        if patched:
            # contexts should match
            from passlib.ext.django.models import password_context
            self.assertEqual(password_context.to_dict(resolve=True),
                             ctx.to_dict(resolve=True))

            # should have patched both places
            from django.contrib.auth.models import check_password as check_password2
            self.assertIs(check_password2, check_password)

        #=======================================================
        # default algorithm
        #=======================================================
        # User.set_password() should use default alg
        user = FakeUser()
        user.set_password(PASS1)
        self.assertTrue(ctx.handler().verify(PASS1, user.password))
        self.assert_valid_password(user)

        # User.check_password() - n/a

        # make_password() should use default alg
        hash = make_password(PASS1)
        self.assertTrue(ctx.handler().verify(PASS1, hash))

        # check_password() - n/a

        #=======================================================
        # empty password behavior
        #=======================================================
        if DJANGO_VERSION < (1,6):
            # NOTE: django 1.4-1.5 treat empty password as invalid

            # User.set_password() should set unusable flag
            user = FakeUser()
            user.set_password('')
            self.assert_unusable_password(user)

            # User.check_password() should never return True
            user = FakeUser()
            user.password = hash = ctx.encrypt("")
            self.assertFalse(user.check_password(""))
            self.assert_valid_password(user, hash)

            # make_password() should reject empty passwords
            self.assertEqual(make_password(""), "!")

            # check_password() should never return True
            self.assertFalse(check_password("", hash))

        else:
            # User.set_password() should use default alg
            user = FakeUser()
            user.set_password('')
            hash = user.password
            self.assertTrue(ctx.handler().verify('', hash))
            self.assert_valid_password(user, hash)

            # User.check_password() should return True
            self.assertTrue(user.check_password(""))
            self.assert_valid_password(user, hash)

            # no make_password()

            # check_password() should return True
            self.assertTrue(check_password("", hash))

        #=======================================================
        # 'unusable flag' behavior
        #=======================================================

        # sanity check via user.set_unusable_password()
        user = FakeUser()
        user.set_unusable_password()
        self.assert_unusable_password(user)

        # ensure User.set_password() sets unusable flag
        user = FakeUser()
        user.set_password(None)
        self.assert_unusable_password(user)

        # User.check_password() should always fail
        self.assertFalse(user.check_password(None))
        self.assertFalse(user.check_password('None'))
        self.assertFalse(user.check_password(''))
        self.assertFalse(user.check_password(PASS1))
        self.assertFalse(user.check_password(WRONG1))
        self.assert_unusable_password(user)

        # make_password() should also set flag
        if DJANGO_VERSION >= (1,6):
            self.assertTrue(make_password(None).startswith("!"))
        else:
            self.assertEqual(make_password(None), "!")

        # check_password() should return False (didn't handle disabled under 1.3)
        self.assertFalse(check_password(PASS1, '!'))

        # identify_hasher() and is_password_usable() should reject it
        self.assertFalse(is_password_usable(user.password))
        if has_identify_hasher:
            self.assertRaises(ValueError, identify_hasher, user.password)

        #=======================================================
        # hash=None
        #=======================================================
        # User.set_password() - n/a

        # User.check_password() - returns False
        user = FakeUser()
        user.password = None
        self.assertFalse(user.check_password(PASS1))
        self.assertFalse(user.has_usable_password())

        # make_password() - n/a

        # check_password() - error
        self.assertFalse(check_password(PASS1, None))

        # identify_hasher() - error
        if has_identify_hasher:
            self.assertRaises(TypeError, identify_hasher, None)

        #=======================================================
        # empty & invalid hash values
        # NOTE: django 1.5 behavior change due to django ticket 18453
        # NOTE: passlib integration tries to match current django version
        #=======================================================
        for hash in ("", # empty hash
                     "$789$foo", # empty identifier
                     ):
            # User.set_password() - n/a

            # User.check_password()
            #   empty
            #   -----
            #   django 1.4 -- blank threw error (fixed in 1.5)
            #   django 1.5 -- blank hash returns False
            #
            #   invalid
            #   -------
            #   django 1.4 -- invalid hash threw error (fixed in 1.5)
            #   django 1.5 -- invalid hash returns False
            user = FakeUser()
            user.password = hash
            if DJANGO_VERSION >= (1,5):
                # returns False for hash
                self.assertFalse(user.check_password(PASS1))
            else:
                # throws error for hash
                self.assertRaises(ValueError, user.check_password, PASS1)

            # verify hash wasn't changed/upgraded during check_password() call
            self.assertEqual(user.password, hash)
            self.assertEqual(user.pop_saved_passwords(), [])

            # User.has_usable_password()
            #   django 1.4 -- invalid/empty usable (fixed in 1.5)
            #   django 1.5 -- invalid/empty no longer usable
            if DJANGO_VERSION < (1,5):
                self.assertTrue(user.has_usable_password())
            else:
                self.assertFalse(user.has_usable_password())

            # make_password() - n/a

            # check_password()
            #   django 1.4 -- invalid/empty hash threw error (fixed in 1.5)
            #   django 1.5 -- invalid/empty hash now returns False
            if DJANGO_VERSION < (1,5):
                self.assertRaises(ValueError, check_password, PASS1, hash)
            else:
                self.assertFalse(check_password(PASS1, hash))

            # identify_hasher() - throws error
            if has_identify_hasher:
                self.assertRaises(ValueError, identify_hasher, hash)

        #=======================================================
        # run through all the schemes in the context,
        # testing various bits of per-scheme behavior.
        #=======================================================
        for scheme in ctx.schemes():
            #-------------------------------------------------------
            # setup constants & imports, pick a sample secret/hash combo
            #-------------------------------------------------------
            handler = ctx.handler(scheme)
            deprecated = ctx._is_deprecated_scheme(scheme)
            assert not deprecated or scheme != ctx.default_scheme()
            try:
                testcase = get_handler_case(scheme)
            except exc.MissingBackendError:
                assert scheme == "bcrypt"
                continue
            assert testcase.handler is handler
            if testcase.is_disabled_handler:
                continue
            if not has_active_backend(handler):
                # TODO: move this above get_handler_case(),
                #       and omit MissingBackendError check.
                assert scheme in ["django_bcrypt", "django_bcrypt_sha256"], "%r scheme should always have active backend" % scheme
                continue
            try:
                secret, hash = sample_hashes[scheme]
            except KeyError:
                while True:
                    secret, hash = testcase('setUp').get_sample_hash()
                    if secret: # don't select blank passwords, especially under django 1.4/1.5
                        break
            other = 'dontletmein'

            # User.set_password() - n/a

            #-------------------------------------------------------
            # User.check_password()+migration against known hash
            #-------------------------------------------------------
            user = FakeUser()
            user.password = hash

            # check against invalid password
            self.assertFalse(user.check_password(None))
            ##self.assertFalse(user.check_password(''))
            self.assertFalse(user.check_password(other))
            self.assert_valid_password(user, hash)

            # check against valid password
            self.assertTrue(user.check_password(secret))

            # check if it upgraded the hash
            # NOTE: needs_update kept separate in case we need to test rounds.
            needs_update = deprecated
            if needs_update:
                self.assertNotEqual(user.password, hash)
                self.assertFalse(handler.identify(user.password))
                self.assertTrue(ctx.handler().verify(secret, user.password))
                self.assert_valid_password(user, saved=user.password)
            else:
                self.assert_valid_password(user, hash)

            # don't need to check rest for most deployments
            if TEST_MODE(max="default"):
                continue

            #-------------------------------------------------------
            # make_password() correctly selects algorithm
            #-------------------------------------------------------
            hash2 = make_password(secret, hasher=passlib_to_hasher_name(scheme))
            self.assertTrue(handler.verify(secret, hash2))

            #-------------------------------------------------------
            # check_password()+setter against known hash
            #-------------------------------------------------------
            # should call setter only if it needs_update
            self.assertTrue(check_password(secret, hash, setter=setter))
            self.assertEqual(setter.popstate(), [secret] if needs_update else [])

            # should not call setter
            self.assertFalse(check_password(other, hash, setter=setter))
            self.assertEqual(setter.popstate(), [])

            ### check preferred kwd is ignored (django 1.4 feature we don't support)
            ##self.assertTrue(check_password(secret, hash, setter=setter, preferred='fooey'))
            ##self.assertEqual(setter.popstate(), [secret])

            # TODO: get_hasher()

            #-------------------------------------------------------
            # identify_hasher() recognizes known hash
            #-------------------------------------------------------
            if has_identify_hasher:
                self.assertTrue(is_password_usable(hash))
                name = hasher_to_passlib_name(identify_hasher(hash).algorithm)
                self.assertEqual(name, scheme)
Пример #3
0
    def test_config(self):
        """test hashing interface

        this function is run against both the actual django code, to
        verify the assumptions of the unittests are correct;
        and run against the passlib extension, to verify it matches
        those assumptions.
        """
        patched, config = self.patched, self.config
        # this tests the following methods:
        #   User.set_password()
        #   User.check_password()
        #   make_password() -- 1.4 only
        #   check_password()
        #   identify_hasher()
        #   User.has_usable_password()
        #   User.set_unusable_password()
        # XXX: this take a while to run. what could be trimmed?

        #  TODO: get_hasher()

        #=======================================================
        # setup helpers & imports
        #=======================================================
        ctx = self.context
        setter = create_mock_setter()
        PASS1 = "toomanysecrets"
        WRONG1 = "letmein"

        if has_django14:
            from passlib.ext.django.utils import hasher_to_passlib_name, passlib_to_hasher_name
            from django.contrib.auth.hashers import check_password, make_password, is_password_usable
            if patched:
                from django.contrib.auth.hashers import identify_hasher
        else:
            from django.contrib.auth.models import check_password

        #=======================================================
        # make sure extension is configured correctly
        #=======================================================
        if patched:
            # contexts should match
            from passlib.ext.django.models import password_context
            self.assertEqual(password_context.to_dict(resolve=True),
                             ctx.to_dict(resolve=True))

            # should have patched both places
            if has_django14:
                from django.contrib.auth.models import check_password as check_password2
                self.assertIs(check_password2, check_password)

        #=======================================================
        # default algorithm
        #=======================================================
        # User.set_password() should use default alg
        user = FakeUser()
        user.set_password(PASS1)
        self.assertTrue(ctx.handler().verify(PASS1, user.password))
        self.assert_valid_password(user)

        # User.check_password() - n/a

        # make_password() should use default alg
        if has_django14:
            hash = make_password(PASS1)
            self.assertTrue(ctx.handler().verify(PASS1, hash))

        # check_password() - n/a

        #=======================================================
        # empty password behavior
        #=======================================================
        if has_django14:
            # NOTE: django 1.4 treats empty password as invalid

            # User.set_password() should set unusable flag
            user = FakeUser()
            user.set_password('')
            self.assert_unusable_password(user)

            # User.check_password() should never return True
            user = FakeUser()
            user.password = hash = ctx.encrypt("")
            self.assertFalse(user.check_password(""))
            self.assert_valid_password(user, hash)

            # make_password() should reject empty passwords
            self.assertEqual(make_password(""), "!")

            # check_password() should never return True
            self.assertFalse(check_password("", hash))

        else:
            # User.set_password() should use default alg
            user = FakeUser()
            user.set_password('')
            hash = user.password
            self.assertTrue(ctx.handler().verify('', hash))
            self.assert_valid_password(user, hash)

            # User.check_password() should return True
            self.assertTrue(user.check_password(""))
            self.assert_valid_password(user, hash)

            # no make_password()

            # check_password() should return True
            self.assertTrue(check_password("", hash))

        #=======================================================
        # 'unusable flag' behavior
        #=======================================================
        if has_django1 or patched:

            # sanity check via user.set_unusable_password()
            user = FakeUser()
            user.set_unusable_password()
            self.assert_unusable_password(user)

            # ensure User.set_password() sets flag
            user = FakeUser()
            user.set_password(None)
            self.assert_unusable_password(user)

            # User.check_password() should always fail
            self.assertFalse(user.check_password(None))
            self.assertFalse(user.check_password(''))
            self.assertFalse(user.check_password(PASS1))
            self.assertFalse(user.check_password(WRONG1))
            self.assert_unusable_password(user)

            # make_password() should also set flag
            if has_django14:
                self.assertEqual(make_password(None), "!")

            # check_password() should return False (didn't handle disabled under 1.3)
            if has_django14 or patched:
                self.assertFalse(check_password(PASS1, '!'))

            # identify_hasher() and is_password_usable() should reject it
            if has_django14:
                self.assertFalse(is_password_usable(user.password))
            if has_django14 and patched:
                self.assertRaises(ValueError, identify_hasher, user.password)

        #=======================================================
        # hash=None
        #=======================================================
        # User.set_password() - n/a

        # User.check_password() - returns False
        user = FakeUser()
        user.password = None
        if has_django14 or patched:
            self.assertFalse(user.check_password(PASS1))
        else:
            self.assertRaises(TypeError, user.check_password, PASS1)
        if has_django1 or patched:
            self.assertFalse(user.has_usable_password())

        # make_password() - n/a

        # check_password() - error
        if has_django14 or patched:
            self.assertFalse(check_password(PASS1, None))
        else:
            self.assertRaises(AttributeError, check_password, PASS1, None)

        # identify_hasher() - error
        if has_django14 and patched:
            self.assertRaises(TypeError, identify_hasher, None)

        #=======================================================
        # invalid hash values
        #=======================================================
        for hash in ("", "$789$foo"):
            # User.set_password() - n/a

            # User.check_password() - blank hash causes error
            user = FakeUser()
            user.password = hash
            if has_django14 or patched or hash:
                self.assertRaises(ValueError, user.check_password, PASS1)
            else:
                # django 1.3 returns False for empty hashes
                self.assertFalse(user.check_password(PASS1))
            self.assert_valid_password(user, hash) # '' counts as valid for some reason

            # make_password() - n/a

            # check_password() - error
            self.assertRaises(ValueError, check_password, PASS1, hash)

            # identify_hasher() - error
            if has_django14 and patched:
                self.assertRaises(ValueError, identify_hasher, hash)

        #=======================================================
        # run through all the schemes in the context,
        # testing various bits of per-scheme behavior.
        #=======================================================
        for scheme in ctx.schemes():
            #-------------------------------------------------------
            # setup constants & imports, pick a sample secret/hash combo
            #-------------------------------------------------------
            handler = ctx.handler(scheme)
            deprecated = ctx._is_deprecated_scheme(scheme)
            assert not deprecated or scheme != ctx.default_scheme()
            try:
                testcase = get_handler_case(scheme)
            except exc.MissingBackendError:
                assert scheme == "bcrypt"
                continue
            assert testcase.handler is handler
            if testcase.is_disabled_handler:
                continue
            if not has_active_backend(handler):
                assert scheme == "django_bcrypt"
                continue
            while True:
                secret, hash = testcase('setUp').get_sample_hash()
                if secret: # don't select blank passwords, special under django
                    break
            other = 'letmein'

            # User.set_password() - n/a

            #-------------------------------------------------------
            # User.check_password()+migration against known hash
            #-------------------------------------------------------
            user = FakeUser()
            user.password = hash

            # check against invalid password
            if has_django1 or patched:
                self.assertFalse(user.check_password(None))
            else:
                self.assertRaises(TypeError, user.check_password, None)
            ##self.assertFalse(user.check_password(''))
            self.assertFalse(user.check_password(other))
            self.assert_valid_password(user, hash)

            # check against valid password
            if has_django0 and isinstance(secret, unicode):
                secret = secret.encode("utf-8")
            self.assertTrue(user.check_password(secret))

            # check if it upgraded the hash
            needs_update = deprecated
            if needs_update:
                self.assertFalse(handler.identify(user.password))
                self.assertTrue(ctx.handler().verify(secret, user.password))
                self.assert_valid_password(user, saved=user.password)
            else:
                self.assert_valid_password(user, hash)

            # don't need to check rest for most deployments
            if TEST_MODE(max="default"):
                continue

            #-------------------------------------------------------
            # make_password() correctly selects algorithm
            #-------------------------------------------------------
            if has_django14:
                hash2 = make_password(secret, hasher=passlib_to_hasher_name(scheme))
                self.assertTrue(handler.verify(secret, hash2))

            #-------------------------------------------------------
            # check_password()+setter against known hash
            #-------------------------------------------------------
            if has_django14 or patched:
                # should call setter only if it needs_update
                self.assertTrue(check_password(secret, hash, setter=setter))
                self.assertEqual(setter.popstate(), [secret] if needs_update else [])

                # should not call setter
                self.assertFalse(check_password(other, hash, setter=setter))
                self.assertEqual(setter.popstate(), [])

                ### check preferred kwd is ignored (django 1.4 feature we don't support)
                ##self.assertTrue(check_password(secret, hash, setter=setter, preferred='fooey'))
                ##self.assertEqual(setter.popstate(), [secret])

            elif patched or scheme != "hex_md5":
                # django 1.3 never called check_password() for hex_md5
                self.assertTrue(check_password(secret, hash))
                self.assertFalse(check_password(other, hash))

            # TODO: get_hasher()

            #-------------------------------------------------------
            # identify_hasher() recognizes known hash
            #-------------------------------------------------------
            if has_django14 and patched:
                self.assertTrue(is_password_usable(hash))
                name = hasher_to_passlib_name(identify_hasher(hash).algorithm)
                self.assertEqual(name, scheme)