Advertisement
opexxx

passgen.py

Mar 31st, 2015
369
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.72 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. """
  4. Generate password.
  5. """
  6. import optparse
  7. import os
  8. import sys
  9. import warnings
  10. import random
  11. import logging
  12. import itertools
  13.  
  14. __author__ = 'Simon Brunning'
  15. __version__ = "0.11"
  16.  
  17. script_name = os.path.basename(sys.argv[0])
  18. usage = script_name + ' [options] args'
  19. description = '''
  20. Generate password.
  21. '''
  22.  
  23. logger = logging.getLogger(script_name)
  24.  
  25. CASE_FUNCTIONS = (lambda x: x.lower(), lambda x: x.upper(), lambda x: x.capitalize())
  26. DEFAULT_PATTERN = "WSW2|W2WS|WS2W|W2SW|WSW2W|W2WSW"
  27. DEFAULT_WORDFILE = "/usr/share/dict/words"
  28. DEFAULT_SYMBOLS = "-=[];\"'\\,./!@$%^*()_:<>?"
  29. DEFAULT_MAX_LENGTH = 128
  30. DEFAULT_WORD_LENGTH = 12
  31.  
  32.  
  33. def main(*argv):
  34.     options, script, args, help = get_options(argv)
  35.     init_logger(options.verbosity)
  36.     logger.debug(options)
  37.  
  38.     patterns = options.pattern.upper().split('|')
  39.     try:
  40.         generated_password = generate_password(options.wordfile, options.symbols, patterns, options.max_length, options.max_word_length)
  41.         print generated_password
  42.     except PasswordsTooShort, passwords_too_short:
  43.         print "Unable to generate password with length %s. " \
  44.               "Try a shorter pattern, or a longer password length." % passwords_too_short.max_length
  45.  
  46.  
  47. def generate_password(wordfile=DEFAULT_WORDFILE, symbol_set=DEFAULT_SYMBOLS, patterns=DEFAULT_PATTERN.upper().split('|'), max_length=DEFAULT_MAX_LENGTH, max_word_length=DEFAULT_WORD_LENGTH):
  48.     words = (word.strip()
  49.              for word
  50.              in random_items(open(wordfile), 999)
  51.              if len(word.strip()) < max_word_length and not word.strip().endswith("'s"))
  52.  
  53.     random_cased_words = (random.choice(CASE_FUNCTIONS)(word) for word in words)
  54.     upper = (word.upper() for word in words)
  55.     lower = (word.lower() for word in words)
  56.     capitalised = (word.capitalize() for word in words)
  57.  
  58.     symbols = repeatfunc(lambda: "".join(random.sample(symbol_set, random.randint(1, 3))))
  59.     spaces = itertools.repeat(" ")
  60.  
  61.     password_element_iterators = {"W": random_cased_words,
  62.                                   "U": upper,
  63.                                   "L": lower,
  64.                                   "C": capitalised,
  65.                                   "S": symbols,
  66.                                   " ": spaces}
  67.     for length in range(1, 10):
  68.         password_element_iterators['%s' % length] = generate_number(length)
  69.  
  70.     pattern = random.choice(patterns)
  71.     logger.debug("pattern, %s", pattern)
  72.  
  73.     while 1:
  74.         try:
  75.             candidate = "".join([password_element_iterators[pattern_element].next() for pattern_element in pattern])
  76.         except StopIteration:
  77.             raise PasswordsTooShort(max_length)
  78.  
  79.         logger.debug("candidate, %s", candidate)
  80.         if len(candidate) <= max_length:
  81.             return candidate
  82.  
  83.  
  84. def random_items(iterable, items_wanted=1):
  85.     """Pick random items with equal probability from an iterable, iterating only once.
  86.  
  87.    http://code.activestate.com/recipes/426332/
  88.    """
  89.     result = [None] * items_wanted
  90.     for index, item in enumerate(iterable):
  91.         if index < items_wanted:
  92.             result[index] = item
  93.         else:
  94.             target = int(random.random() * (index + 1))
  95.             if target < items_wanted:
  96.                 result[target] = item
  97.     random.shuffle(result)
  98.     return result
  99.  
  100.  
  101. def repeatfunc(func, times=None, *args):
  102.     """Repeat calls to func with specified arguments.
  103.  
  104.    Example:  repeatfunc(random.random)
  105.  
  106.    http://docs.python.org/2/library/itertools.html#recipes
  107.    """
  108.     if times is None:
  109.         return itertools.starmap(func, itertools.repeat(args))
  110.     return itertools.starmap(func, itertools.repeat(args, times))
  111.  
  112.  
  113. def generate_number(length):
  114.     return repeatfunc(lambda: ("%%0%sd" % length) % random.randint(0, 10 ** length - 1))
  115.  
  116.  
  117. class PasswordsTooShort(Exception):
  118.     def __init__(self, max_length, *args, **kwargs):
  119.         super(PasswordsTooShort, self).__init__(*args, **kwargs)
  120.         self.max_length = max_length
  121.  
  122.  
  123. def get_options(argv):
  124.     """Get options and arguments from argv string."""
  125.     parser = optparse.OptionParser(usage=usage, version=__version__)
  126.     parser.description = description
  127.     parser.add_option("-v", "--verbosity", action="count", default=0,
  128.                       help="Specify up to three times to increase verbosity, i.e. -v to see warnings, -vv for "
  129.                            "information messages, or -vvv for debug messages.")
  130.  
  131.     parser.add_option("-p", "--pattern", action="store", default=DEFAULT_PATTERN,
  132.                       help="Password pattern. "
  133.                            "W=word, "
  134.                            "U=upper-cased word, "
  135.                            "L=lower-cased word, "
  136.                            "C=capitalised word, "
  137.                            "S=symbols, "
  138.                            "n=number with n (1-9) digits, "
  139.                            "' '=space. "
  140.                            "Multiple patterns may be provided, separated by '|' characters. "
  141.                            "One pattern will be selected at random.  Defaults to %default. "
  142.                            "http://xkcd.com/936/ style passwords can be generated with 'l l l l'.")
  143.     parser.add_option("-w", "--wordfile", action="store", default=DEFAULT_WORDFILE,
  144.                       help="Text file containing list of words. Defaults to %default.")
  145.     parser.add_option("-s", "--symbols", action="store", default=DEFAULT_SYMBOLS,
  146.                       help="List of symbols to pick from to include in password. Defaults to %default.")
  147.     parser.add_option("-l", "--max-length", action="store", type="int", default=DEFAULT_MAX_LENGTH,
  148.                       help="Maximum length for generated password. Defaults to %default.")
  149.     parser.add_option("--max-word-length", action="store", type="int", default=DEFAULT_WORD_LENGTH,
  150.                       help="Maximum length for word elements. Defaults to %default.")
  151.  
  152.     options, args = parser.parse_args(list(argv))
  153.     script, args = args[0], args[1:]
  154.     return options, script, args, parser.format_help()
  155.  
  156.  
  157. def init_logger(verbosity, stream=sys.stdout):
  158.     """Initialize logger and warnings according to verbosity argument.
  159.    Verbosity levels of 0-3 supported."""
  160.     is_not_debug = verbosity <= 2
  161.     level = [logging.ERROR, logging.WARNING, logging.INFO][verbosity] if is_not_debug else logging.DEBUG
  162.     log_format = '%(message)s' if is_not_debug \
  163.         else '%(asctime)s %(levelname)-8s %(name)s %(module)s.py:%(funcName)s():%(lineno)d %(message)s'
  164.     logging.basicConfig(level=level, format=log_format, stream=stream)
  165.     if is_not_debug: warnings.filterwarnings('ignore')
  166.  
  167.  
  168. if __name__ == "__main__":
  169.     sys.exit(main(*sys.argv))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement