class TestHashing: @pytest.fixture(scope='class') def pw(self) -> str: return generate_password(8) @pytest.mark.slow def test_salt_generation(self, pw): """The same password should be encrypted differently for each invocation.""" hashes = tuple(hash_password(pw) for i in range(10)) assert len(hashes) == len(set(hashes)) @pytest.mark.parametrize( 'hash_method', [ldap_des_crypt, ldap_sha512_crypt, ldap_md5, ldap_salted_sha1]) def test_hash_verification(self, pw, hash_method): """Test that all currently employed password schemes are supported by the verification function.""" encrypted = hash_method.hash(pw) assert verify_password(pw, encrypted), \ f"{hash_method.name}: '{encrypted}' should verify '{pw}'" @pytest.mark.parametrize('random_pw', (generate_password(8) for i in range(10))) def test_hash_type(self, random_pw): """Assert that the password scheme used for new passwords is ldap_sha512_crypt.""" expected_hash_method = ldap_sha512_crypt encrypted = hash_password(random_pw) assert expected_hash_method.identify(encrypted), \ f"Expected hashes for method {expected_hash_method.name}, got: {encrypted}"
def reset_password(user, processor): plain_password = user_helper.generate_password(12) user.password = plain_password message = deferred_gettext(u"Password was reset") log_user_event(author=processor, user=user, message=message.to_json()) return plain_password
def test_hash_type(self): """Assert that the password scheme used for new passwords is ldap_sha512_crypt.""" expected_hash_method = ldap_sha512_crypt for pw in (generate_password(8) for i in range(10)): encrypted = hash_password(pw) self.assertTrue(expected_hash_method.identify(encrypted), "Expected hashes for method {}, got: {}" .format(expected_hash_method.name, encrypted))
def test_salt_generation(self): """The same password should be encrypted differently for each invocation.""" pw = generate_password(8) hashes = tuple(hash_password(pw) for i in range(10)) self.assertEqual( len(hashes), len(set(hashes)), )
def test_hash_verification(self): """Test that all currently employed password schemes are supported by the verification function.""" pw = generate_password(8) for hash_method in (ldap_des_crypt, ldap_sha512_crypt, ldap_md5, ldap_salted_sha1): encrypted = hash_method.encrypt(pw) self.assertTrue(verify_password(pw, encrypted), "{}: '{}' should verify '{}'" .format(hash_method.name, encrypted, pw))
def test_set_and_verify_password(self, user, session): password = generate_password(4) user.password = password session.flush() assert user.check_password(password) assert User.verify_and_get(user.login, password) == user assert User.verify_and_get(user.login, password + "_wrong") is None # TODO reduce set of examples, this is excessive. # Also, why do we depend on `generate_password` instead of testing it separately? # All of this is very unperformant with little benefit. for length in range(4, 10): for cnt in range(1, 3): pw = generate_password(length) if pw == password: continue assert not user.check_password(pw) assert User.verify_and_get(user.login, pw) is None
def test_0030_generate_plain(self): pw_list = [] hash_list = [] for num in range(1, 500): pw = generate_password(9) self.assertEqual(len(pw), 9) self.assertFalse(pw in pw_list) pw_list.append(pw) pw_hash = hash_password(pw) self.assertFalse(pw_hash in hash_list) hash_list.append(pw_hash)
def test_0020_set_and_verify_password(self): password = generate_password(4) self.user.password = password session.session.commit() self.assertTrue(self.user.check_password(password)) self.assertEqual(user.User.verify_and_get(self.user.login, password), self.user) self.assertIsNone(user.User.verify_and_get(self.user.login, password + "_wrong")) for length in range(4, 10): for cnt in range(1, 3): pw = generate_password(length) if pw == password: continue self.assertFalse(self.user.check_password(pw)) self.assertIsNone(user.User.verify_and_get(self.user.login, pw))
def test_0020_set_and_verify_password(self): password = generate_password(4) self.user.password = password session.session.commit() self.assertTrue(self.user.check_password(password)) self.assertEqual(user.User.verify_and_get(self.user.login, password), self.user) self.assertIsNone(user.User.verify_and_get(self.user.login, password + "_wrong")) for length in range(4, 10): for cnt in range(1, 3): pw = generate_password(length) if pw == password: continue self.assertFalse(self.user.check_password(pw)) self.assertIsNone(user.User.verify_and_get(self.user.login, pw))
def reset_password(user, processor): if not can_target(user, processor): raise PermissionError("cannot reset password of a user with a" " greater or equal permission level.") plain_password = user_helper.generate_password(12) user.password = plain_password message = deferred_gettext(u"Password was reset") log_user_event(author=processor, user=user, message=message.to_json()) return plain_password
def test_0020_set_and_verify_password(self): u = user.User.q.get(1) password = generate_password(4) pw_hash = hash_password(password) u.set_password(password) session.session.commit() u = user.User.q.get(1) self.assertTrue(u.check_password(password)) self.assertIsNotNone(user.User.verify_and_get(u.login, password)) self.assertEqual(user.User.verify_and_get(u.login, password), u) self.assertIsNone(user.User.verify_and_get(password, u.login)) for length in range(0, 10): for cnt in range(1, 3): pw = generate_password(length) if pw != password: self.assertFalse(u.check_password(pw)) self.assertIsNone(user.User.verify_and_get(u.login, pw))
def test_0010_password_hash_validator(self): u = user.User.q.get(1) password = generate_password(4) pw_hash = hash_password(password) def set_hash(h): u.passwd_hash = h set_hash(pw_hash) session.session.commit() self.assertRaisesRegexp(AssertionError, "A password-hash with les than 9 chars is not correct!", set_hash, password) session.session.commit() self.assertRaisesRegexp(AssertionError, "Cannot clear the password hash!", set_hash, None) session.session.commit()
def create_user(name, login, email, birthdate, groups, processor, address): """Create a new member Create a new user with a generated password, finance- and unix account, and make him member of the `config.member_group` and `config.network_access_group`. :param str name: The full name of the user (e.g. Max Mustermann) :param str login: The unix login for the user :param str email: E-Mail address of the user :param Date birthdate: Date of birth :param PropertyGroup groups: The initial groups of the new user :param User processor: The processor :param Address address: Where the user lives. May or may not come from a room. :return: """ now = session.utcnow() plain_password = user_helper.generate_password(12) # create a new user new_user = User( login=login, name=name, email=email, registered_at=now, account=Account(name="", type="USER_ASSET"), password=plain_password, birthdate=birthdate, address=address ) account = UnixAccount(home_directory="/home/{}".format(login)) new_user.unix_account = account with session.session.begin(subtransactions=True): session.session.add(new_user) session.session.add(account) new_user.account.name = deferred_gettext(u"User {id}").format( id=new_user.id).to_json() for group in groups: make_member_of(new_user, group, processor, closed(now, None)) log_user_event(author=processor, message=deferred_gettext(u"User created.").to_json(), user=new_user) return new_user, plain_password
def test_password_hash_validator(self, user, session): password = generate_password(4) pw_hash = hash_password(password) user.passwd_hash = pw_hash session.flush() with session.begin_nested(), \ pytest.raises(AssertionError, match="A password-hash with less than 9 chars is not correct!"): user.passwd_hash = password session.flush() with pytest.raises(AssertionError, match="Cannot clear the password hash!"): user.passwd_hash = None session.flush()
def test_password_hash_validator(self): password = generate_password(4) pw_hash = hash_password(password) self.user.passwd_hash = pw_hash session.session.commit() with self.assertRaisesRegexp( AssertionError, "A password-hash with less than 9 chars " "is not correct!"): self.user.passwd_hash = password session.session.commit() with self.assertRaisesRegexp(AssertionError, "Cannot clear the " "password hash!"): self.user.passwd_hash = None session.session.commit()
def test_0010_password_hash_validator(self): password = generate_password(4) pw_hash = hash_password(password) self.user.passwd_hash = pw_hash session.session.commit() with self.assertRaisesRegexp(AssertionError, "A password-hash with less than 9 chars " "is not correct!"): self.user.passwd_hash = password session.session.commit() with self.assertRaisesRegexp(AssertionError, "Cannot clear the " "password hash!"): self.user.passwd_hash = None session.session.commit()
def __init__(self, *args, **kwargs): self.hashes = [] def crypt_pw(pw): return "{crypt}" + python_crypt(pw, generate_crypt_salt(2)) self.methods = {"crypt": crypt_pw, "CRYPT": ldap_sha1_crypt.encrypt, "MD5": ldap_md5_crypt.encrypt, "SSHA": ldap_salted_sha1.encrypt} for length in range(4,20): pw = generate_password(length) hash_dict = {"plain": pw} for method in self.methods: hash_dict[method] = self.methods[method](pw) self.hashes.append(hash_dict) super(Test_020_PasswdHashes, self).__init__(*args, **kwargs)
def test_length(self, length: int): """Assert that generated passwords are of correct length""" assert len(generate_password(length)) == length
def test_uniqueness(self): """Assert that new passwords are generated for each invocation""" passwords = tuple(generate_password(8) for i in range(100)) self.assertEqual(len(passwords), len(set(passwords)))
def test_length(self): """Assert that generated passwords are of correct length""" for length in range(2, 30): self.assertEqual(len(generate_password(length)), length)
def move_in(name, login, email, dormitory, level, room_number, mac, processor, host_name=None): """ This function creates a new user, assign him to a room and creates some initial groups and transactions. :param name: The full name of the user. (Max Mustermann) :param login: The unix login for the user. :param email: E-Mail address of the user. :param dormitory: The dormitory the user moves in. :param level: The level the user moves in. :param room_number: The room number the user moves in. :param mac: The mac address of the users pc. :param host_name: An optional Hostname for the users pc. :return: The new user object. """ room = Room.q.filter_by(number=room_number, level=level, dormitory=dormitory).one() # create a new user new_user = User(login=login, name=name, email=email, room=room, registration_date=datetime.now()) plain_password = user.generate_password(12) #TODO: print plain password on paper instead print u"new password: "******"move_in"] for membership in conf["group_memberships"]: group = Group.q.filter(Group.name == membership["name"]).one() start_date = datetime.now() if membership.get("offset"): start_date += timedelta(membership["offset"]) new_membership = create_membership(start_date=start_date, end_date=None, group=group, user=new_user) if membership.get("duration"): assert membership["duration"] > 0 new_membership.end_date = datetime.now() + timedelta( membership["duration"]) setup_user_finance_account(new_user, processor) move_in_user_log_entry = create_user_log_entry(author=processor, message=conf["log_message"], timestamp=datetime.now(), user=new_user) return new_user
def create_user(name, login, email, birthdate, groups, processor, address, passwd_hash=None, send_confirm_mail: bool = False): """Create a new member Create a new user with a generated password, finance- and unix account, and make him member of the `config.member_group` and `config.network_access_group`. :param str name: The full name of the user (e.g. Max Mustermann) :param str login: The unix login for the user :param str email: E-Mail address of the user :param Date birthdate: Date of birth :param PropertyGroup groups: The initial groups of the new user :param Optional[User] processor: The processor :param Address address: Where the user lives. May or may not come from a room. :param passwd_hash: Use password hash instead of generating a new password :param send_confirm_mail: If a confirmation mail should be send to the user :return: """ now = session.utcnow() plain_password = user_helper.generate_password(12) # create a new user new_user = User(login=login, name=name, email=email, registered_at=now, account=Account(name="", type="USER_ASSET"), password=plain_password, wifi_password=generate_wifi_password(), birthdate=birthdate, address=address) processor = processor if processor is not None else new_user if passwd_hash: new_user.passwd_hash = passwd_hash plain_password = None account = UnixAccount(home_directory="/home/{}".format(login)) new_user.unix_account = account with session.session.begin(subtransactions=True): session.session.add(new_user) session.session.add(account) new_user.account.name = deferred_gettext(u"User {id}").format( id=new_user.id).to_json() for group in groups: make_member_of(new_user, group, processor, closed(now, None)) log_user_event(author=processor, message=deferred_gettext(u"User created.").to_json(), user=new_user) user_send_mail(new_user, UserCreatedTemplate(), True) if email is not None and send_confirm_mail: send_confirmation_email(new_user) return new_user, plain_password
def generate_wifi_password(): return user_helper.generate_password(12)
def test_0010_pw_length(self): for i in range(0, 100): length = random.randint(2, 12) pw = generate_password(length) self.assertEqual(len(pw), length) self.pws.append(pw)
def test_0020_unique(self): for i in range(0, 100): self.pws.append(generate_password(8)) self.assertEqual(len(self.pws), len(set(self.pws)))
def move_in(name, login, email, dormitory, level, room_number, mac, processor, host_name=None): """ This function creates a new user, assign him to a room and creates some initial groups and transactions. :param name: The full name of the user. (Max Mustermann) :param login: The unix login for the user. :param email: E-Mail address of the user. :param dormitory: The dormitory the user moves in. :param level: The level the user moves in. :param room_number: The room number the user moves in. :param mac: The mac address of the users pc. :param host_name: An optional Hostname for the users pc. :return: The new user object. """ room = Room.q.filter_by(number=room_number, level=level, dormitory=dormitory).one() # create a new user new_user = User( login=login, name=name, email=email, room=room, registration_date=datetime.now() ) plain_password = user.generate_password(12) #TODO: print plain password on paper instead print u"new password: "******"move_in"] for membership in conf["group_memberships"]: group = Group.q.filter(Group.name == membership["name"]).one() start_date = datetime.now() if membership.get("offset"): start_date += timedelta(membership["offset"]) new_membership = create_membership( start_date=start_date, end_date=None, group=group, user=new_user ) if membership.get("duration"): assert membership["duration"] > 0 new_membership.end_date = datetime.now() + timedelta(membership["duration"]) setup_user_finance_account(new_user, processor) move_in_user_log_entry = create_user_log_entry( author=processor, message=conf["log_message"], timestamp=datetime.now(), user=new_user ) return new_user
def pw(self) -> str: return generate_password(8)
def test_length(self): """Assert that generated passwords are of correct length""" for length in range(2, 30): self.assertEqual(len(generate_password(length)), length)
def test_uniqueness(self): """Assert that new passwords are generated for each invocation""" passwords = tuple(generate_password(8) for i in range(100)) self.assertEqual(len(passwords), len(set(passwords)))
def test_salt_generation(self): """The same password should be encrypted differently for each invocation.""" pw = generate_password(8) hashes = tuple(hash_password(pw) for i in range(10)) self.assertEqual(len(hashes), len(set(hashes)),)