Created
March 6, 2020 07:54
-
-
Save NiklasRosenstein/003a3a418366e9e41a8d36b5c3ea88ca to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import string | |
import random | |
DEFAULT_POOLS = [ | |
string.ascii_lowercase, | |
string.ascii_uppercase, | |
string.digits, | |
'_!@$' | |
] | |
def generate_password(length=14, pools=None): | |
""" Generates a password that contains at least 1 character out of every of | |
the specified character pools. The character pools should be distinct sets. | |
The minimum length for the password is the number of pools provided. """ | |
if pools is None: | |
pools = DEFAULT_POOLS | |
assert length >= len(pools) | |
password = ''.join(random.choice(''.join(pools)) for _ in range(length)) | |
counts = ( | |
{'count': sum(password.count(x) for x in pool), 'pool': pool} | |
for pool in pools | |
) | |
counts = sorted(counts, key=lambda x: x['count']) | |
for data in filter(lambda x: x['count'] == 0, counts): | |
source = next((x for x in reversed(counts) if x['count'] > 1)) | |
source['count'] -= 1 | |
indices = (password.find(x) for x in source['pool']) | |
index = next(filter(lambda i: i >= 0, indices)) | |
password = password[:index] + random.choice(data['pool']) + password[index+1:] | |
assert all(any(x in password for x in pool) for pool in pools) | |
return password |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment