Skip to content

Instantly share code, notes, and snippets.

@NiklasRosenstein
Created March 6, 2020 07:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NiklasRosenstein/003a3a418366e9e41a8d36b5c3ea88ca to your computer and use it in GitHub Desktop.
Save NiklasRosenstein/003a3a418366e9e41a8d36b5c3ea88ca to your computer and use it in GitHub Desktop.
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