/home/alex/dev/passwd-gen.py (1)
#!/usr/local/bin/python3
# ------------------------------------------------------------------------------
# passwd-gen.py
# =============
#
# Scope Native
# Copyright (C) 2024 by RaySoft, Zurich, Switzerland
# License GNU General Public License (GPL) 2.0
# https://www.gnu.org/licenses/gpl2.txt
#
# ------------------------------------------------------------------------------
DEFAULT_LENGTH = 25
DEFAULT_AMOUNT = 10
# ------------------------------------------------------------------------------
PROGRAM_NAME = 'passwd-gen'
PROGRAM_VERSION = '0.2'
# ------------------------------------------------------------------------------
from argparse import ArgumentParser
from random import SystemRandom
from re import search, sub, IGNORECASE
from string import ascii_uppercase, ascii_lowercase, digits
from sys import exit, stderr
# ------------------------------------------------------------------------------
def main():
parser = ArgumentParser(
prog=PROGRAM_NAME, allow_abbrev=False,
description='Generate random password',
epilog='If the character set is not defined, all characters are used',
)
parser.add_argument(
'-V', '--version', action='version',
version=f'%(prog)s {PROGRAM_VERSION}',
)
parser.add_argument(
'-a', '--amount', action='store',
type=int, metavar='NUMBER', default=DEFAULT_AMOUNT,
help=f'amount of the generated passwords '
f'[default: {DEFAULT_AMOUNT:d}]',
)
parser.add_argument(
'-l', '--length', action='store',
type=int, metavar='NUMBER', default=DEFAULT_LENGTH,
help=f'length in characters of the generated passwords '
f'[default: {DEFAULT_LENGTH:d}]',
)
parser.add_argument(
'-L', '--lower', action='store_true',
help='use lowercase characters',
)
parser.add_argument(
'-N', '--number', action='store_true',
help='use numbers',
)
parser.add_argument(
'-S', '--special', action='store_true',
help='use special characters',
)
parser.add_argument(
'-U', '--upper', action='store_true',
help='use lowercase characters',
)
args = parser.parse_args()
if args.length < 2:
print('Password length must be at least 2 characters.', file=stderr)
parser.print_usage(file=stderr)
return 1
if args.amount < 1:
print('Password amount must be at least 1.', file=stderr)
parser.print_usage(file=stderr)
return 1
charset = ''
counter = 0
types = {
'upper': [ascii_uppercase + 'ÄÖÜ', 1/4],
'lower': [ascii_lowercase + 'äöü', 1/4],
'number': [digits, 1/8],
'special': ['_.:,;!?&%$@#=/*+-', 1/8],
}
if not (args.upper or args.lower or args.number or args.special):
for type in types.keys():
args.__dict__[type] = True
for type in types.keys():
if args.__dict__[type]:
charset += types[type][0]
types[type][1] = round(args.length * (1 - types[type][1]))
urandom = SystemRandom()
while counter < args.amount:
passwd = ''.join(urandom.sample(charset, k=args.length))
if (args.upper or args.lower) and not (
search(
f"^[{types['lower'][0]}].*[{types['lower'][0]}]$", passwd,
flags=IGNORECASE,
)
):
continue
for type in types.keys():
rest_length = len(sub(f'[{types[type][0]}]', '', passwd))
if args.__dict__[type] and rest_length >= types[type][1]:
continue
print(passwd)
counter += 1
return 0
# ------------------------------------------------------------------------------
if __name__ == '__main__':
return_value = main()
exit(return_value)
Usage
~/dev/passwd-gen.py --amount=2 --length=20
Output:
piF%3?-u!wHo1Se6JL?n
C5_LRwE35+iY0=IuYqja