def test_pbkdf2_upgrade_new_hasher(self): self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) hasher = get_hasher('default') self.assertNotEqual(hasher.iterations, 1) state = {'upgraded': False} def setter(password): state['upgraded'] = True with self.settings(PASSWORD_HASHERS=[ 'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher' ]): encoded = make_password('letmein') algo, iterations, salt, hash = encoded.split('$', 3) self.assertEqual(iterations, '1') # Check that no upgrade is triggerd self.assertTrue(check_password('letmein', encoded, setter)) self.assertFalse(state['upgraded']) # Revert to the old iteration count and check if the password would get # updated to the new iteration count. with self.settings(PASSWORD_HASHERS=[ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher' ]): self.assertTrue(check_password('letmein', encoded, setter)) self.assertTrue(state['upgraded'])
def test_pbkdf2_upgrade(self): self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) hasher = get_hasher('default') self.assertNotEqual(hasher.iterations, 1) old_iterations = hasher.iterations try: # Generate a password with 1 iteration. hasher.iterations = 1 encoded = make_password('letmein') algo, iterations, salt, hash = encoded.split('$', 3) self.assertEqual(iterations, '1') state = {'upgraded': False} def setter(password): state['upgraded'] = True # Check that no upgrade is triggerd self.assertTrue(check_password('letmein', encoded, setter)) self.assertFalse(state['upgraded']) # Revert to the old iteration count and ... hasher.iterations = old_iterations # ... check if the password would get updated to the new iteration count. self.assertTrue(check_password('letmein', encoded, setter)) self.assertTrue(state['upgraded']) finally: hasher.iterations = old_iterations
def render(self, name, value, attrs): encoded = value if not is_password_usable(encoded): return "None" final_attrs = self.build_attrs(attrs) encoded = smart_str(encoded) if len(encoded) == 32 and '$' not in encoded: hasher = get_hasher('md5') else: algorithm = encoded.split('$', 1)[0] hasher = get_hasher(algorithm) summary = "" for key, value in hasher.safe_summary(encoded).iteritems(): summary += "<strong>%(key)s</strong>: %(value)s " % { "key": key, "value": value } return mark_safe("<div%(attrs)s>%(summary)s</div>" % { "attrs": flatatt(final_attrs), "summary": summary })
def test_pbkdf2_upgrade_new_hasher(self): self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) hasher = get_hasher('default') self.assertNotEqual(hasher.iterations, 1) state = {'upgraded': False} def setter(password): state['upgraded'] = True with self.settings(PASSWORD_HASHERS=[ 'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']): encoded = make_password('letmein') algo, iterations, salt, hash = encoded.split('$', 3) self.assertEqual(iterations, '1') # Check that no upgrade is triggerd self.assertTrue(check_password('letmein', encoded, setter)) self.assertFalse(state['upgraded']) # Revert to the old iteration count and check if the password would get # updated to the new iteration count. with self.settings(PASSWORD_HASHERS=[ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']): self.assertTrue(check_password('letmein', encoded, setter)) self.assertTrue(state['upgraded'])
def _create_django_hasher(self, django_name): """ helper to create new django hasher by name. wraps underlying django methods. """ # if we haven't patched django, can use it directly module = sys.modules.get("passlib.ext.django.models") if module is None or not module.adapter.patched: from django.contrib.auth.hashers import get_hasher return get_hasher(django_name) # We've patched django's get_hashers(), so calling django's get_hasher() # or get_hashers_by_algorithm() would only land us back here. # As non-ideal workaround, have to use original get_hashers(), get_hashers = module.adapter._manager.getorig("django.contrib.auth.hashers:get_hashers").__wrapped__ for hasher in get_hashers(): if hasher.algorithm == django_name: return hasher # hardcode a few for cases where get_hashers() look won't work. path = self._builtin_django_hashers.get(django_name) if path: if "." not in path: path = "django.contrib.auth.hashers." + path from django.utils.module_loading import import_string return import_string(path)() raise ValueError("unknown hasher: %r" % django_name)
def demote_user(username): target_user = User.objects.filter(username=username) assert len(target_user) == 1 , \ "There is no such user or the database is corrupted" hasher = get_hasher('pph') hasher.load() # for safety purposes, we will scan the database to guarantee that there # is already a threshold of users in order to recover the secert after # unlocking. target_threshold = hasher.threshold number_of_threshold_accounts = 0 all_users = User.objects.all() for user in all_users: sharenumber = user.password.split('$')[1] sharenumber = int(sharenumber) if sharenumber > 0: number_of_threshold_accounts += 1 assert number_of_threshold_accounts > target_threshold # now, perform the demotion for user in target_user: encoded = user.password new_password = hasher.demote_hash(encoded) user.password = new_password user.save()
def _test_scrypt_upgrade(self, attr, summary_key, new_value): hasher = get_hasher("scrypt") self.assertEqual(hasher.algorithm, "scrypt") self.assertNotEqual(getattr(hasher, attr), new_value) old_value = getattr(hasher, attr) try: # Generate hash with attr set to the new value. setattr(hasher, attr, new_value) encoded = make_password("lètmein", "seasalt", "scrypt") attr_value = hasher.safe_summary(encoded)[summary_key] self.assertEqual(attr_value, new_value) state = {"upgraded": False} def setter(password): state["upgraded"] = True # No update is triggered. self.assertIs(check_password("lètmein", encoded, setter, "scrypt"), True) self.assertIs(state["upgraded"], False) # Revert to the old value. setattr(hasher, attr, old_value) # Password is updated. self.assertIs(check_password("lètmein", encoded, setter, "scrypt"), True) self.assertIs(state["upgraded"], True) finally: setattr(hasher, attr, old_value)
def new(self, request): user = request.user hasher = get_hasher("default") nana = ExternalNewSerializer(data=request.data) if nana.is_valid(): accesstoken = user.id # TODO: THIS should be a unique string for every user, their own private secret uniqueexternalid = hasher.encode(accesstoken, nana.validated_data["name"]) else: raise ValidationError("Something is wrong here") # Checking if this external already exists in Data external = External.objects.filter(uniqueid=uniqueexternalid) if not external.exists(): newexternaldict = copy.copy(request.data) newexternaldict["uniqueid"] = uniqueexternalid newexternal = ExternalSerializer(data=newexternaldict) if newexternal.is_valid(): instance = newexternal.save() self.publish(newexternal, "create") return Response(newexternal.data) else: raise ValidationError("You didnt provide the correct data") else: newexternal = ExternalSerializer(external.first()) self.publish(newexternal, "update") return Response(newexternal.data)
def test_check_password_upgrade(self): """Don't update password if adjusting hash iteration password_changed() shouldn't be called if User.check_password() triggers a hash iteration upgrade. """ user = User.objects.create_user(email="*****@*****.**", password="******") initial_password = user.password self.assertTrue(user.check_password("foo")) hasher = get_hasher("default") self.assertEqual("pbkdf2_sha256", hasher.algorithm) old_iterations = hasher.iterations try: # Upgrade the password iterations hasher.iterations = old_iterations + 1 with patch( "django.contrib.auth.password_validation.password_changed", ) as pw_changed: user.check_password("foo") self.assertEqual(pw_changed.call_count, 0) self.assertNotEqual(initial_password, user.password) finally: hasher.iterations = old_iterations
def test_bcrypt_upgrade(self): hasher = get_hasher("bcrypt") self.assertEqual("bcrypt", hasher.algorithm) self.assertNotEqual(hasher.rounds, 4) old_rounds = hasher.rounds try: # Generate a password with 4 rounds. hasher.rounds = 4 encoded = make_password("letmein", hasher="bcrypt") rounds = hasher.safe_summary(encoded)["work factor"] self.assertEqual(rounds, "04") state = {"upgraded": False} def setter(password): state["upgraded"] = True # Check that no upgrade is triggered. self.assertTrue(check_password("letmein", encoded, setter, "bcrypt")) self.assertFalse(state["upgraded"]) # Revert to the old rounds count and ... hasher.rounds = old_rounds # ... check if the password would get updated to the new count. self.assertTrue(check_password("letmein", encoded, setter, "bcrypt")) self.assertTrue(state["upgraded"]) finally: hasher.rounds = old_rounds
def test_pbkdf2_upgrade(self): hasher = get_hasher("default") self.assertEqual("pbkdf2_sha256", hasher.algorithm) self.assertNotEqual(hasher.iterations, 1) old_iterations = hasher.iterations try: # Generate a password with 1 iteration. hasher.iterations = 1 encoded = make_password("letmein") algo, iterations, salt, hash = encoded.split("$", 3) self.assertEqual(iterations, "1") state = {"upgraded": False} def setter(password): state["upgraded"] = True # Check that no upgrade is triggered self.assertTrue(check_password("letmein", encoded, setter)) self.assertFalse(state["upgraded"]) # Revert to the old iteration count and ... hasher.iterations = old_iterations # ... check if the password would get updated to the new iteration count. self.assertTrue(check_password("letmein", encoded, setter)) self.assertTrue(state["upgraded"]) finally: hasher.iterations = old_iterations
def render(self, name, value, attrs): encoded = value if not is_password_usable(encoded): return "None" final_attrs = self.build_attrs(attrs) encoded = smart_str(encoded) if len(encoded) == 32 and '$' not in encoded: algorithm = 'unsalted_md5' else: algorithm = encoded.split('$', 1)[0] try: hasher = get_hasher(algorithm) except ValueError: summary = "<strong>Invalid password format or unknown hashing algorithm.</strong>" else: summary = "" for key, value in hasher.safe_summary(encoded).iteritems(): summary += "<strong>%(key)s</strong>: %(value)s " % {"key": ugettext(key), "value": value} return mark_safe("<div%(attrs)s>%(summary)s</div>" % {"attrs": flatatt(final_attrs), "summary": summary})
def _test_argon2_upgrade(self, attr, summary_key, new_value): hasher = get_hasher('argon2') self.assertEqual('argon2', hasher.algorithm) self.assertNotEqual(getattr(hasher, attr), new_value) old_value = getattr(hasher, attr) try: # Generate hash with attr set to 1 setattr(hasher, attr, new_value) encoded = make_password('letmein', hasher='argon2') attr_value = hasher.safe_summary(encoded)[summary_key] self.assertEqual(attr_value, new_value) state = {'upgraded': False} def setter(password): state['upgraded'] = True # Check that no upgrade is triggered. self.assertTrue(check_password('letmein', encoded, setter, 'argon2')) self.assertFalse(state['upgraded']) # Revert to the old rounds count and ... setattr(hasher, attr, old_value) # ... check if the password would get updated to the new count. self.assertTrue(check_password('letmein', encoded, setter, 'argon2')) self.assertTrue(state['upgraded']) finally: setattr(hasher, attr, old_value)
def test_bcrypt_upgrade(self): hasher = get_hasher('bcrypt') self.assertEqual('bcrypt', hasher.algorithm) self.assertNotEqual(hasher.rounds, 4) old_rounds = hasher.rounds try: # Generate a password with 4 rounds. hasher.rounds = 4 encoded = make_password('letmein', hasher='bcrypt') rounds = hasher.safe_summary(encoded)['work factor'] self.assertEqual(rounds, '04') state = {'upgraded': False} def setter(password): state['upgraded'] = True # Check that no upgrade is triggered. self.assertTrue(check_password('letmein', encoded, setter, 'bcrypt')) self.assertFalse(state['upgraded']) # Revert to the old rounds count and ... hasher.rounds = old_rounds # ... check if the password would get updated to the new count. self.assertTrue(check_password('letmein', encoded, setter, 'bcrypt')) self.assertTrue(state['upgraded']) finally: hasher.rounds = old_rounds
def _test_argon2_upgrade(self, attr, summary_key, new_value): hasher = get_hasher("argon2") self.assertEqual("argon2", hasher.algorithm) self.assertNotEqual(getattr(hasher, attr), new_value) old_value = getattr(hasher, attr) try: # Generate hash with attr set to 1 setattr(hasher, attr, new_value) encoded = make_password("letmein", hasher="argon2") attr_value = hasher.safe_summary(encoded)[summary_key] self.assertEqual(attr_value, new_value) state = {"upgraded": False} def setter(password): state["upgraded"] = True # No upgrade is triggered. self.assertTrue( check_password("letmein", encoded, setter, "argon2")) self.assertFalse(state["upgraded"]) # Revert to the old rounds count and ... setattr(hasher, attr, old_value) # ... check if the password would get updated to the new count. self.assertTrue( check_password("letmein", encoded, setter, "argon2")) self.assertTrue(state["upgraded"]) finally: setattr(hasher, attr, old_value)
def test_bcrypt_upgrade(self): hasher = get_hasher("bcrypt") self.assertEqual("bcrypt", hasher.algorithm) self.assertNotEqual(hasher.rounds, 4) old_rounds = hasher.rounds try: # Generate a password with 4 rounds. hasher.rounds = 4 encoded = make_password("letmein", hasher="bcrypt") rounds = hasher.safe_summary(encoded)["work factor"] self.assertEqual(rounds, 4) state = {"upgraded": False} def setter(password): state["upgraded"] = True # No upgrade is triggered. self.assertTrue( check_password("letmein", encoded, setter, "bcrypt")) self.assertFalse(state["upgraded"]) # Revert to the old rounds count and ... hasher.rounds = old_rounds # ... check if the password would get updated to the new count. self.assertTrue( check_password("letmein", encoded, setter, "bcrypt")) self.assertTrue(state["upgraded"]) finally: hasher.rounds = old_rounds
def clean_password(self): hasher = get_hasher(algorithm='sha1') hashed_password = hasher.encode(self.cleaned_data['password'], SALT) bingo_boards = BingoBoard.objects.filter( password=hashed_password).select_related() # check, that no User gets two BingoBoards from the same Game, # just because another User used the same password. # Games, which contain two BingoBoards with the given password # are filtered, so these BingoBoards cannot be claimed by anyone. game_ids = set() duplicate_game_ids = set() for board in bingo_boards: if board.game.id in game_ids: duplicate_game_ids.add(board.game.id) else: game_ids.add(board.game.id) # filter for user=None to prevent stealing already claimed boards bingo_boards.exclude( game__id__in=duplicate_game_ids).filter( user=None).update(user=self.user) return hashed_password
def test_pbkdf2_upgrade_new_hasher(self): hasher = get_hasher("default") self.assertEqual("pbkdf2_sha256", hasher.algorithm) self.assertNotEqual(hasher.iterations, 1) state = {"upgraded": False} def setter(password): state["upgraded"] = True with self.settings(PASSWORD_HASHERS=[ "auth_tests.test_hashers.PBKDF2SingleIterationHasher" ]): encoded = make_password("letmein") algo, iterations, salt, hash = encoded.split("$", 3) self.assertEqual(iterations, "1") # No upgrade is triggered self.assertTrue(check_password("letmein", encoded, setter)) self.assertFalse(state["upgraded"]) # Revert to the old iteration count and check if the password would get # updated to the new iteration count. with self.settings(PASSWORD_HASHERS=[ "django.contrib.auth.hashers.PBKDF2PasswordHasher", "auth_tests.test_hashers.PBKDF2SingleIterationHasher", ]): self.assertTrue(check_password("letmein", encoded, setter)) self.assertTrue(state["upgraded"])
def test_bcrypt_upgrade(self): hasher = get_hasher('bcrypt') self.assertEqual('bcrypt', hasher.algorithm) self.assertNotEqual(hasher.rounds, 4) old_rounds = hasher.rounds try: # Generate a password with 4 rounds. hasher.rounds = 4 encoded = make_password('letmein', hasher='bcrypt') rounds = hasher.safe_summary(encoded)['work factor'] self.assertEqual(rounds, '04') state = {'upgraded': False} def setter(password): state['upgraded'] = True # No upgrade is triggered. self.assertTrue(check_password('letmein', encoded, setter, 'bcrypt')) self.assertFalse(state['upgraded']) # Revert to the old rounds count and ... hasher.rounds = old_rounds # ... check if the password would get updated to the new count. self.assertTrue(check_password('letmein', encoded, setter, 'bcrypt')) self.assertTrue(state['upgraded']) finally: hasher.rounds = old_rounds
def test_argon2(self): encoded = make_password("lètmein", hasher="argon2") self.assertTrue(is_password_usable(encoded)) self.assertTrue(encoded.startswith("argon2$argon2id$")) self.assertTrue(check_password("lètmein", encoded)) self.assertFalse(check_password("lètmeinz", encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "argon2") # Blank passwords blank_encoded = make_password("", hasher="argon2") self.assertTrue(blank_encoded.startswith("argon2$argon2id$")) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password("", blank_encoded)) self.assertFalse(check_password(" ", blank_encoded)) # Old hashes without version attribute encoded = ( "argon2$argon2i$m=8,t=1,p=1$c29tZXNhbHQ$gwQOXSNhxiOxPOA0+PY10P9QFO" "4NAYysnqRt1GSQLE55m+2GYDt9FEjPMHhP2Cuf0nOEXXMocVrsJAtNSsKyfg") self.assertTrue(check_password("secret", encoded)) self.assertFalse(check_password("wrong", encoded)) # Old hashes with version attribute. encoded = "argon2$argon2i$v=19$m=8,t=1,p=1$c2FsdHNhbHQ$YC9+jJCrQhs5R6db7LlN8Q" self.assertIs(check_password("secret", encoded), True) self.assertIs(check_password("wrong", encoded), False) # Salt entropy check. hasher = get_hasher("argon2") encoded_weak_salt = make_password("lètmein", "iodizedsalt", "argon2") encoded_strong_salt = make_password("lètmein", hasher.salt(), "argon2") self.assertIs(hasher.must_update(encoded_weak_salt), True) self.assertIs(hasher.must_update(encoded_strong_salt), False)
def demote_user(username): target_user = User.objects.filter(username=username) assert len(target_user) == 1,\ "There is no such user or the database is corrupted" hasher = get_hasher('pph') hasher.load() # for safety purposes, we will scan the database to guarantee that there # is already a threshold of users in order to recover the secert after # unlocking. target_threshold = hasher.threshold number_of_threshold_accounts = 0 all_users = User.objects.all() for user in all_users: sharenumber = user.password.split('$')[1] sharenumber = int(sharenumber) if sharenumber > 0: number_of_threshold_accounts += 1 assert number_of_threshold_accounts > target_threshold # now, perform the demotion for user in target_user: encoded = user.password new_password = hasher.demote_hash(encoded) user.password = new_password user.save()
def render(self, name, value, attrs): encoded = value if not is_password_usable(encoded): return "None" final_attrs = self.build_attrs(attrs) encoded = smart_str(encoded) if len(encoded) == 32 and '$' not in encoded: algorithm = 'unsalted_md5' else: algorithm = encoded.split('$', 1)[0] try: hasher = get_hasher(algorithm) except ValueError: summary = "<strong>Invalid password format or unknown hashing algorithm.</strong>" else: summary = "" for key, value in hasher.safe_summary(encoded).iteritems(): summary += "<strong>%(key)s</strong>: %(value)s " % { "key": ugettext(key), "value": value } return mark_safe("<div%(attrs)s>%(summary)s</div>" % { "attrs": flatatt(final_attrs), "summary": summary })
def test_pbkdf2_upgrade_new_hasher(self): hasher = get_hasher("default") self.assertEqual("pbkdf2_sha256", hasher.algorithm) self.assertNotEqual(hasher.iterations, 1) state = {"upgraded": False} def setter(password): state["upgraded"] = True with self.settings(PASSWORD_HASHERS=["auth_tests.test_hashers.PBKDF2SingleIterationHasher"]): encoded = make_password("letmein") algo, iterations, salt, hash = encoded.split("$", 3) self.assertEqual(iterations, "1") # Check that no upgrade is triggered self.assertTrue(check_password("letmein", encoded, setter)) self.assertFalse(state["upgraded"]) # Revert to the old iteration count and check if the password would get # updated to the new iteration count. with self.settings( PASSWORD_HASHERS=[ "django.contrib.auth.hashers.PBKDF2PasswordHasher", "auth_tests.test_hashers.PBKDF2SingleIterationHasher", ] ): self.assertTrue(check_password("letmein", encoded, setter)) self.assertTrue(state["upgraded"])
def test_pbkdf2_upgrade(self): hasher = get_hasher("default") self.assertEqual("pbkdf2_sha256", hasher.algorithm) self.assertNotEqual(hasher.iterations, 1) old_iterations = hasher.iterations try: # Generate a password with 1 iteration. hasher.iterations = 1 encoded = make_password("letmein") algo, iterations, salt, hash = encoded.split("$", 3) self.assertEqual(iterations, "1") state = {"upgraded": False} def setter(password): state["upgraded"] = True # No upgrade is triggered self.assertTrue(check_password("letmein", encoded, setter)) self.assertFalse(state["upgraded"]) # Revert to the old iteration count and ... hasher.iterations = old_iterations # ... check if the password would get updated to the new iteration count. self.assertTrue(check_password("letmein", encoded, setter)) self.assertTrue(state["upgraded"]) finally: hasher.iterations = old_iterations
def clean_password(self): if self.cleaned_data['password']: hasher = get_hasher(algorithm='sha1') hashed_password = hasher.encode( self.cleaned_data['password'], SALT) return hashed_password else: return None
def clean_password(self): if self.cleaned_data['password']: hasher = get_hasher(algorithm='sha1') hashed_password = hasher.encode(self.cleaned_data['password'], SALT) return hashed_password else: return None
def test_no_upgrade_on_incorrect_pass(self): self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) for algo in ('sha1', 'md5'): encoded = make_password('lètmein', hasher=algo) state = {'upgraded': False} def setter(): state['upgraded'] = True self.assertFalse(check_password('WRONG', encoded, setter)) self.assertFalse(state['upgraded'])
def test_upgrade(self): self.assertEqual('fastpbkdf2_sha256', get_hasher('default').algorithm) for algo in ('sha1', 'md5'): encoded = make_password('letmein', hasher=algo) state = {'upgraded': False} def setter(password): state['upgraded'] = True self.assertTrue(check_password('letmein', encoded, setter)) self.assertTrue(state['upgraded'])
def getPasswordHash(password): """Gives the hash of the given password string""" if password is None : return None else: hasher = get_hasher('default') salt = hasher.salt() pass_hash = make_password(password,salt) return pass_hash
def test_argon2_decode(self): salt = 'abcdefghijk' encoded = make_password('lètmein', salt=salt, hasher='argon2') hasher = get_hasher('argon2') decoded = hasher.decode(encoded) self.assertEqual(decoded['memory_cost'], hasher.memory_cost) self.assertEqual(decoded['parallelism'], hasher.parallelism) self.assertEqual(decoded['salt'], salt) self.assertEqual(decoded['time_cost'], hasher.time_cost)
def test_argon2_decode(self): salt = "abcdefghijk" encoded = make_password("lètmein", salt=salt, hasher="argon2") hasher = get_hasher("argon2") decoded = hasher.decode(encoded) self.assertEqual(decoded["memory_cost"], hasher.memory_cost) self.assertEqual(decoded["parallelism"], hasher.parallelism) self.assertEqual(decoded["salt"], salt) self.assertEqual(decoded["time_cost"], hasher.time_cost)
def handle(self, *args, **options): updates = 0 hasher = get_hasher('unsalted_md5') password = make_password('456', '', hasher) updates = User.objects.update(password=password) print(self.style.SQL_COLTYPE('%d passwords changed to "456"' % updates)) self.stdout.write('Successfully %d passwords changed to 456"' % updates)
def clean(self): username = self.cleaned_data.get('username') password = self.cleaned_data.get('password') # see if password is encoded as a hash if password: # a hash is in the format: <algorithm>$<iterations>$<hash> parts = password.split('$') if len(parts) >= 3 and len(password) <= 128: # most likely a hash hasher = get_hasher() if parts[0] == hasher.algorithm: # it is a hash, authenticate encoded = password try: # retrieve current password pwd = TemporaryPassword.objects.get( user__username=username) password = pwd.password valid_until = pwd.created_on + timedelta(seconds=120) # invalidate login if password is expired if timezone.now() > valid_until: password = None except ObjectDoesNotExist: password = None # check password if password is not None and check_password( password, encoded): # confirm user login allowed self.confirm_login_allowed(pwd.user) # valid login return self.cleaned_data # otherwise it is an invalid login raise forms.ValidationError( self.error_messages['invalid_login'], code='invalid_login', params={'username': self.username_field.verbose_name}, ) # if not a hash, call super to validate password return super().clean()
def save(self, *args, **kwargs): if (self.org is None and self.user is None): raise Exception("You need to provide a user or org as owner") elif (self.org is not None and self.user is not None): raise Exception("A petition can have only one owner") else: if not self.salt: hasher = get_hasher() self.salt = hasher.salt().decode('utf-8') super(Petition, self).save(*args, **kwargs)
def get_required_password_hash_specs(): """Returns the algorithm and iterations to be used for new password hashes""" default_hasher = get_hasher() return { 'algo': default_hasher.algorithm, 'iterations': default_hasher.iterations }
def set_client_secret(self, password): if password is None: raise AttributeError('Password can not be empty') if self.client_auth_type == self.CLIENT_AUTH_TYPE_SECRET_JWT: # need secret in plain text ! self.client_hashed_secret = password else: hasher = get_hasher('default') salt = hasher.salt() self.client_hashed_secret = hasher.encode(password, salt)
def test_safesummary(self): """ Test to see whether safe_summary works. """ hasher = get_hasher('legacy') self.assertEquals( hasher.safe_summary('legacy$fd054714ea93060e80c3cac98ff47a424a2acfd1'), { 'algorithm': 'legacy', 'hash': 'fd0547**********************************' } )
def test_no_upgrade_on_incorrect_pass(self): self.assertEqual("pbkdf2_sha256", get_hasher("default").algorithm) for algo in ("sha1", "md5"): encoded = make_password("letmein", hasher=algo) state = {"upgraded": False} def setter(): state["upgraded"] = True self.assertFalse(check_password("WRONG", encoded, setter)) self.assertFalse(state["upgraded"])
def test_upgrade(self): self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm) for algo in ('sha1', 'md5'): with self.subTest(algo=algo): encoded = make_password('lètmein', hasher=algo) state = {'upgraded': False} def setter(password): state['upgraded'] = True self.assertTrue(check_password('lètmein', encoded, setter)) self.assertTrue(state['upgraded'])
def test_check_password_calls_harden_runtime(self): hasher = get_hasher("default") encoded = make_password("letmein") with mock.patch.object(hasher, "harden_runtime"), mock.patch.object(hasher, "must_update", return_value=True): # Correct password supplied, no hardening needed check_password("letmein", encoded) self.assertEqual(hasher.harden_runtime.call_count, 0) # Wrong password supplied, hardening needed check_password("wrong_password", encoded) self.assertEqual(hasher.harden_runtime.call_count, 1)
def _get_hasher(algorithm): """wrapper to call django.contrib.auth.hashers:get_hasher()""" import sys module = sys.modules.get("passlib.ext.django.models") if module is None: # we haven't patched django, so just import directly from django.contrib.auth.hashers import get_hasher else: # we've patched django, so have to use patch manager to retrieve # original get_hasher() function... get_hasher = module._manager.getorig("django.contrib.auth.hashers:get_hasher") return get_hasher(algorithm)
def make_password(password, salt=None, hasher='default'): UNUSABLE_PASSWORD_PREFIX = '!' UNUSABLE_PASSWORD_SUFFIX_LENGTH = 40 if password is None: return UNUSABLE_PASSWORD_PREFIX + get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH) hasher = get_hasher(hasher) if not salt: salt = hasher.salt() return password, hasher.encode(password, salt)
def test_no_upgrade_on_incorrect_pass(self): self.assertEqual("pbkdf2_sha256", get_hasher("default").algorithm) for algo in ("pbkdf2_sha1", "md5"): with self.subTest(algo=algo): encoded = make_password("lètmein", hasher=algo) state = {"upgraded": False} def setter(): state["upgraded"] = True self.assertFalse(check_password("WRONG", encoded, setter)) self.assertFalse(state["upgraded"])
def render(self, name, value, attrs): encoded = value if not is_password_usable(encoded): return "None" final_attrs = self.build_attrs(attrs) encoded = smart_str(encoded) if len(encoded) == 32 and '$' not in encoded: hasher = get_hasher('md5') else: algorithm = encoded.split('$', 1)[0] hasher = get_hasher(algorithm) summary = "" for key, value in hasher.safe_summary(encoded).iteritems(): summary += "<strong>%(key)s</strong>: %(value)s " % {"key": key, "value": value} return mark_safe("<div%(attrs)s>%(summary)s</div>" % {"attrs": flatatt(final_attrs), "summary": summary})
def get_prep_value(self, value, prepared=False): if not prepared and value: if not getattr(self, 'salt', False): self.salt = get_hasher('default').salt() # Check that it isn't already hashed so we don't double hash. This # is an issue because the value passed is the value returned # to_python() unless you are doing a .update() in which case # to_python() is not called and the value is passed in raw. if not isinstance(value, HashedData): value = self._hash(value, salt=self.salt) self.salt = False # dump salt after saving. return value
def get_prep_value(self, value, prepared=False): if not prepared and value: if not getattr(self, 'salt', False): self.salt = get_hasher('default').salt() # Check that it isn't already hashed so we don't double hash. This # is an issue because the value passed is the value returned # to_python() unless you are doing a .update() in which case # to_python() is not called and the value is passed in raw. if not isinstance(value, HashedData): value = make_password(value, salt=self.salt) self.salt = False # dump salt after saving. return value
def test_check_password_calls_harden_runtime(self): hasher = get_hasher('default') encoded = make_password('letmein') with mock.patch.object(hasher, 'harden_runtime'), \ mock.patch.object(hasher, 'must_update', return_value=True): # Correct password supplied, no hardening needed check_password('letmein', encoded) self.assertEqual(hasher.harden_runtime.call_count, 0) # Wrong password supplied, hardening needed check_password('wrong_password', encoded) self.assertEqual(hasher.harden_runtime.call_count, 1)
def promote_user(username): target_user = User.objects.filter(username=username) assert len(target_user) == 1, \ "there is no such user or the database is corrupted" hasher = get_hasher('pph') hasher.load() for user in target_user: encoded = user.password new_password = hasher.promote_hash(encoded) user.password = new_password user.save()
def to_python(self, value): if isinstance(value, HashedData): return value if value == '' or value is None: return HashedData('') if not isinstance(value, (str, unicode)): raise ValueError('HashField only takes str or unicode.') hasher = get_hasher('default') # Check to see if this is a hash already (likely loaded from the DB), # if not then we need to hash it and save the salt so later when we try # to save the hash, the same value we see is what is put in the DB. if not value.startswith(hasher.algorithm): self.salt = hasher.salt() value = self._hash(value, salt=self.salt) return HashedData(value)
def handle(self, *args, **options): """ Converts passwords with the default wordpress phpass algorithm to be readable by Django. """ # command to run: python manage.py convert_wp_passwords hasher = get_hasher('phpass') users = get_user_model().objects.filter(password__startswith='$P$B') for user in users: user.password = hasher.from_orig(user.password) user.save() print user.pk print "%s passwords converted" % len(users)
def test_pbkdf2_harden_runtime(self): hasher = get_hasher("default") self.assertEqual("pbkdf2_sha256", hasher.algorithm) with mock.patch.object(hasher, "iterations", 1): encoded = make_password("letmein") with mock.patch.object(hasher, "iterations", 6), mock.patch.object(hasher, "encode", side_effect=hasher.encode): hasher.harden_runtime("wrong_password", encoded) # Encode should get called once ... self.assertEqual(hasher.encode.call_count, 1) # ... with the original salt and 5 iterations. algorithm, iterations, salt, hash = encoded.split("$", 3) expected_call = (("wrong_password", salt, 5),) self.assertEqual(hasher.encode.call_args, expected_call)
def hydrate_password(self, bundle): """ Encode new passwords. """ if 'password' in bundle.data and bundle.data['password']: algo = 'sha1' hasher = get_hasher(algo) salt = hasher.salt() hsh = hasher.encode(bundle.data['password'], salt) bundle.data['password'] = '******' % (algo, salt, hsh) else: # A blank password marks the user as unable to login bundle.data['password'] = '' return bundle
def test_pbkdf2_harden_runtime(self): hasher = get_hasher('default') self.assertEqual('pbkdf2_sha256', hasher.algorithm) with mock.patch.object(hasher, 'iterations', 1): encoded = make_password('letmein') with mock.patch.object(hasher, 'iterations', 6), \ mock.patch.object(hasher, 'encode', side_effect=hasher.encode): hasher.harden_runtime('wrong_password', encoded) # Encode should get called once ... self.assertEqual(hasher.encode.call_count, 1) # ... with the original salt and 5 iterations. algorithm, iterations, salt, hash = encoded.split('$', 3) expected_call = (('wrong_password', salt, 5),) self.assertEqual(hasher.encode.call_args, expected_call)
def test_bcrypt_harden_runtime(self): hasher = get_hasher("bcrypt") self.assertEqual("bcrypt", hasher.algorithm) with mock.patch.object(hasher, "rounds", 4): encoded = make_password("letmein", hasher="bcrypt") with mock.patch.object(hasher, "rounds", 6), mock.patch.object(hasher, "encode", side_effect=hasher.encode): hasher.harden_runtime("wrong_password", encoded) # Increasing rounds from 4 to 6 means an increase of 4 in workload, # therefore hardening should run 3 times to make the timing the # same (the original encode() call already ran once). self.assertEqual(hasher.encode.call_count, 3) # Get the original salt (includes the original workload factor) algorithm, data = encoded.split("$", 1) expected_call = (("wrong_password", force_bytes(data[:29])),) self.assertEqual(hasher.encode.call_args_list, [expected_call] * 3)
def clean_password(self): hasher = get_hasher(algorithm='sha1') hashed_password = hasher.encode(self.cleaned_data['password'], SALT) if not self.game or self.game.is_expired(): raise forms.ValidationError( _("The game is expired, please create a new board.")) bingo_boards = BingoBoard.objects.filter(game=self.game, password=hashed_password, user=None) if bingo_boards.count() > 0: # if two users have the same password, # just return the first board. self.cleaned_data['bingo_board'] = bingo_boards[0] else: raise forms.ValidationError( _(u"No active board with this password." " Try again, or create a new one.")) return hashed_password
def test_bcrypt_harden_runtime(self): hasher = get_hasher('bcrypt') self.assertEqual('bcrypt', hasher.algorithm) with mock.patch.object(hasher, 'rounds', 4): encoded = make_password('letmein', hasher='bcrypt') with mock.patch.object(hasher, 'rounds', 6), \ mock.patch.object(hasher, 'encode', side_effect=hasher.encode): hasher.harden_runtime('wrong_password', encoded) # Increasing rounds from 4 to 6 means an increase of 4 in workload, # therefore hardening should run 3 times to make the timing the # same (the original encode() call already ran once). self.assertEqual(hasher.encode.call_count, 3) # Get the original salt (includes the original workload factor) algorithm, data = encoded.split('$', 1) expected_call = (('wrong_password', data[:29].encode()),) self.assertEqual(hasher.encode.call_args_list, [expected_call] * 3)