flufl.password-1.2.1/0000775000175000017500000000000011744121150014742 5ustar barrybarry00000000000000flufl.password-1.2.1/MANIFEST.in0000664000175000017500000000006411707351173016511 0ustar barrybarry00000000000000include *.py global-include *.txt *.rst prune build flufl.password-1.2.1/setup.py0000664000175000017500000000430611744120742016465 0ustar barrybarry00000000000000# Copyright (C) 2004-2012 by Barry A. Warsaw # # This file is part of flufl.password. # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . import distribute_setup distribute_setup.use_setuptools() from setup_helpers import ( description, get_version, long_description, require_python) from setuptools import setup, find_packages require_python(0x20600f0) __version__ = get_version('flufl/password/__init__.py') setup( name='flufl.password', version=__version__, namespace_packages=['flufl'], packages=find_packages(), include_package_data=True, maintainer='Barry Warsaw', maintainer_email='barry@python.org', description=description('README.rst'), long_description=long_description('README.rst', 'flufl/password/NEWS.rst'), license='LGPLv3', url='http://launchpad.net/flufl.password', download_url='https://launchpad.net/flufl.password/+download', test_suite='flufl.password.tests', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: ' 'GNU Lesser General Public License v3 or later (LGPLv3+)', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Operating System :: MacOS :: MacOS X', 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', ] ) flufl.password-1.2.1/setup.cfg0000664000175000017500000000022511744121150016562 0ustar barrybarry00000000000000[build_sphinx] source_dir = flufl/password [upload_docs] upload_dir = build/sphinx/html [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 flufl.password-1.2.1/setup_helpers.py0000664000175000017500000001206211744120710020200 0ustar barrybarry00000000000000# Copyright (C) 2009-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """setup.py helper functions.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'description', 'find_doctests', 'get_version', 'long_description', 'require_python', ] import os import re import sys DEFAULT_VERSION_RE = re.compile(r'(?P\d+\.\d+(?:\.\d+)?)') EMPTYSTRING = '' __version__ = '2.1' def require_python(minimum): """Require at least a minimum Python version. The version number is expressed in terms of `sys.hexversion`. E.g. to require a minimum of Python 2.6, use:: >>> require_python(0x206000f0) :param minimum: Minimum Python version supported. :type minimum: integer """ if sys.hexversion < minimum: hversion = hex(minimum)[2:] if len(hversion) % 2 != 0: hversion = '0' + hversion split = list(hversion) parts = [] while split: parts.append(int(''.join((split.pop(0), split.pop(0))), 16)) major, minor, micro, release = parts if release == 0xf0: print('Python {0}.{1}.{2} or better is required'.format( major, minor, micro)) else: print('Python {0}.{1}.{2} ({3}) or better is required'.format( major, minor, micro, hex(release)[2:])) sys.exit(1) def get_version(filename, pattern=None): """Extract the __version__ from a file without importing it. While you could get the __version__ by importing the module, the very act of importing can cause unintended consequences. For example, Distribute's automatic 2to3 support will break. Instead, this searches the file for a line that starts with __version__, and extract the version number by regular expression matching. By default, two or three dot-separated digits are recognized, but by passing a pattern parameter, you can recognize just about anything. Use the `version` group name to specify the match group. :param filename: The name of the file to search. :type filename: string :param pattern: Optional alternative regular expression pattern to use. :type pattern: string :return: The version that was extracted. :rtype: string """ if pattern is None: cre = DEFAULT_VERSION_RE else: cre = re.compile(pattern) with open(filename) as fp: for line in fp: if line.startswith('__version__'): mo = cre.search(line) assert mo, 'No valid __version__ string found' return mo.group('version') raise AssertionError('No __version__ assignment found') def find_doctests(start='.', extension='.rst'): """Find separate-file doctests in the package. This is useful for Distribute's automatic 2to3 conversion support. The `setup()` keyword argument `convert_2to3_doctests` requires file names, which may be difficult to track automatically as you add new doctests. :param start: Directory to start searching in (default is cwd) :type start: string :param extension: Doctest file extension (default is .txt) :type extension: string :return: The doctest files found. :rtype: list """ doctests = [] for dirpath, dirnames, filenames in os.walk(start): doctests.extend(os.path.join(dirpath, filename) for filename in filenames if filename.endswith(extension)) return doctests def long_description(*filenames): """Provide a long description.""" res = [''] for filename in filenames: with open(filename) as fp: for line in fp: res.append(' ' + line) res.append('') res.append('\n') return EMPTYSTRING.join(res) def description(filename): """Provide a short description.""" # This ends up in the Summary header for PKG-INFO and it should be a # one-liner. It will get rendered on the package page just below the # package version header but above the long_description, which ironically # gets stuff into the Description header. It should not include reST, so # pick out the first single line after the double header. with open(filename) as fp: for lineno, line in enumerate(fp): if lineno < 3: continue line = line.strip() if len(line) > 0: return line flufl.password-1.2.1/flufl/0000775000175000017500000000000011744121150016052 5ustar barrybarry00000000000000flufl.password-1.2.1/flufl/__init__.py0000664000175000017500000000160611707351173020177 0ustar barrybarry00000000000000# Copyright (C) 2004-2012 by Barry A. Warsaw # # This file is part of flufl.password. # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . # this is a namespace package try: import pkg_resources pkg_resources.declare_namespace(__name__) except ImportError: import pkgutil __path__ = pkgutil.extend_path(__path__, __name__) flufl.password-1.2.1/flufl/password/0000775000175000017500000000000011744121150017714 5ustar barrybarry00000000000000flufl.password-1.2.1/flufl/password/_registry.py0000664000175000017500000000402711710373107022304 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Scheme registry.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'BadPasswordSchemeError', 'lookup', 'register', ] _registry = {} class BadPasswordSchemeError(Exception): """An unknown password scheme tag was given.""" def register(scheme_class): """Register a scheme by its tag. This is intended to be used as a class descriptor. :param scheme_class: A password scheme. :type scheme_class: A class which has a `TAG` attribute. The tag may contain space-separated words with additional information, but the scheme will be registered using the first word as the key. :return: scheme_class """ key = scheme_class.TAG.split()[0] _registry[key] = scheme_class return scheme_class def lookup(tag): """Return a scheme class by its tag. :param tag: The tag to search for. The tag may contain space-separated words with additional information, but the scheme will be looked up using the first word as the key. :type tag: string :return: The matching scheme class. :rtype: class :raises BadPasswordSchemeError: when the named scheme can't be found. """ key = tag.split()[0] try: return _registry[key] except KeyError: raise BadPasswordSchemeError(tag) flufl.password-1.2.1/flufl/password/docs/0000775000175000017500000000000011744121150020644 5ustar barrybarry00000000000000flufl.password-1.2.1/flufl/password/docs/using.rst0000664000175000017500000001272511707351173022543 0ustar barrybarry00000000000000================================ Using the flufl.password package ================================ This package comes with a number of password hashing schemes. Some are more secure, while others provide for useful debugging. A hashed password follows the syntax promoted in `RFC 2307`_ (as best I can tell), having a basic format of ``{scheme}hashed_password``. Hashing a password ================== You can create a secure hashed password using the default scheme, which includes random data. >>> from flufl.password import make_secret >>> show(make_secret('my password')) {SSHA}... You can also create a hashed password using one of the other built-in schemes. >>> from flufl.password.schemes import SHAPasswordScheme >>> show(make_secret('my password', SHAPasswordScheme)) {SHA}ovj3-hlaCAoipokEHaqPIET58zY= Available schemes ----------------- There are several built-in schemes to choose from, which run the gamut from useful for debugging to variously higher levels of security. * The *no password* scheme throws away the password and always returns the empty string, but with a properly formatted password. >>> from flufl.password.schemes import NoPasswordScheme >>> show(make_secret('my password', NoPasswordScheme)) {NONE} * The *clear text* scheme returns the original password in clear text, but properly formatted. >>> from flufl.password.schemes import ClearTextPasswordScheme >>> show(make_secret('my password', ClearTextPasswordScheme)) {CLEARTEXT}my password * The *SHA1* password scheme encodes the SHA1 hash of the password. >>> show(make_secret('my password', SHAPasswordScheme)) {SHA}ovj3-hlaCAoipokEHaqPIET58zY= * The *salted SHA1* scheme adds a random salt to the password's digest. >>> from flufl.password.schemes import SSHAPasswordScheme >>> show(make_secret('my password', SSHAPasswordScheme)) {SSHA}... * There is an `RFC 2898`_ password encoding scheme. >>> from flufl.password.schemes import PBKDF2PasswordScheme >>> show(make_secret('my password', PBKDF2PasswordScheme)) {PBKDF2 SHA 2000}... Custom schemes -------------- It's also easy enough to create your own scheme. It must implement a static `make_secret()` method, which you can inherit from a common base class. The class must also have a `TAG` attribute which gives the unique name of this hashing scheme. The scheme should be registered so that it can be found by its tag for verification purposes. This can be done using the `@register` descriptor. :: >>> from codecs import getencoder >>> from flufl.password import register >>> from flufl.password.schemes import PasswordScheme >>> @register ... class MyScheme(PasswordScheme): ... TAG = 'CAESAR' ... @staticmethod ... def make_secret(password): ... # In Python 3, this is a string-to-string encoding. The ... # caller already turned `password` into a byte string, so ... # we have to pass it back through a string to rotate it. ... # We also can't just call .encode('rot_13') on the string ... # because Python 3.2 chokes on the returned string (it expects ... # a bytes object to be returned). Sigh. ... as_string = password.decode('utf-8') ... encoder = getencoder('rot_13') ... return encoder(as_string)[0].encode('utf-8') >>> show(make_secret('my password', MyScheme)) {CAESAR}zl cnffjbeq Hashed passwords are always bytes. >>> isinstance(make_secret('my password', MyScheme), bytes) True Verifying a password ==================== When the user entered their original password, you hashed it using one of the schemes mentioned above. You are only storing this hashed password in your database. The user now wants to log in, so she provides you with her plain text password. You want to see if they match. The easiest way to do this is to give both the plain text password the user just typed, and the hash password you have in your database. >>> from flufl.password import verify >>> verify(b'{SHA}ovj3-hlaCAoipokEHaqPIET58zY=', 'my password') True Of course, if they enter the wrong password, it does not verify. >>> verify(b'{SHA}ovj3-hlaCAoipokEHaqPIET58zY=', 'your password') False Your custom hashing scheme must implement the `check_response()` API in order to support password verification. The `PasswordScheme` base class supports the most obvious implementation of this, which serves for most schemes. For example, the Caesar scheme does not need to implement a `check_response()` method. >>> verify(b'{CAESAR}zl cnffjbeq', 'my password') True User-friendly passwords ======================= This package also provides a convenient utility for generating *user friendly* passwords. These passwords gather random input and translate them into pairs of vowel-consonant (or consonant-vowel) syllables. It then strings together enough of these syllables to match the requested password length. In theory, this produces relatively secure passwords that are easier to pronounce and remember. The security claims of these generated passwords have not been evaluated. >>> from flufl.password import generate >>> my_password = generate(10) >>> len(my_password) 10 >>> sum(1 for c in my_password if c in 'aeiou') 5 >>> sum(1 for c in my_password if c not in 'aeiou') 5 .. _`RFC 2307`: http://www.faqs.org/rfcs/rfc2307.html .. _`RFC 2898`: http://www.faqs.org/rfcs/rfc2898.html flufl.password-1.2.1/flufl/password/docs/__init__.py0000664000175000017500000000000011707351173022754 0ustar barrybarry00000000000000flufl.password-1.2.1/flufl/password/_verify.py0000664000175000017500000000331511710373107021737 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Verification.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'verify', ] from flufl.password._registry import lookup from flufl.password._utils import split def verify(hashed, cleartext): """Verify whether the clear text response matches the hashed challenge. :param hashed: The hashed password. :type hashed: bytes :param cleartext: The clear text password to match against. :type cleartext: bytes or utf-8 encoded string. :return: Flag indicating whether the password matched. :rtype: bool :raises BadPasswordSchemeError: when the indicated scheme cannot be found. :raises BadPasswordFormatError: when the hashed parameter is not properly formatted as ``{SCHEME}password``. """ if not isinstance(cleartext, bytes): cleartext = cleartext.encode('utf-8') scheme, secret = split(hashed) scheme_class = lookup(scheme.upper()) return scheme_class.check_response(secret, cleartext, scheme) flufl.password-1.2.1/flufl/password/_utils.py0000664000175000017500000000236711710373107021601 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Various utilities.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'BadPasswordFormatError', 'split', ] import re SCHEME_RE = r'{(?P[^}]+?)}(?P.*)'.encode() class BadPasswordFormatError(Exception): """A badly formatted password hash was given.""" def split(hashed): mo = re.match(SCHEME_RE, hashed, re.IGNORECASE) if not mo: raise BadPasswordFormatError scheme, secret = mo.groups(('scheme', 'rest')) return scheme.decode('utf-8'), secret flufl.password-1.2.1/flufl/password/NEWS.rst0000664000175000017500000000143411744120652021232 0ustar barrybarry00000000000000======================= NEWS for flufl.password ======================= 1.2.1 (2012-04-19) ================== * Add classifiers to setup.py and make the long description more compatible with the Cheeseshop. * Other changes to make the Cheeseshop page look nicer. (LP: #680136) * setup_helper.py version 2.1. 1.2 (2012-01-23) ================ * Fix some packaging issues. * Remove tox.ini. * Bump Copyright years. * Update standard template. * Eliminate the need to use 2to3, and fix some Python 3 deprecations. 1.1.1 (2012-01-01) ================== * Ensure all built-in schemes are registered by importing them in the __init__.py file. 1.1 (2011-12-31) ================ * Add user-friendly password generation API. 1.0 (2011-12-31) ================ * Initial release. flufl.password-1.2.1/flufl/password/__init__.py0000664000175000017500000000254411744120661022040 0ustar barrybarry00000000000000# Copyright (C) 2004-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Package init.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'BadPasswordFormatError', 'BadPasswordSchemeError', '__version__', 'generate', 'lookup', 'make_secret', 'register', 'verify', ] __version__ = '1.2.1' from ._hash import make_secret from ._generate import generate from ._registry import BadPasswordSchemeError, lookup, register from ._utils import BadPasswordFormatError from ._verify import verify # Register the built-in schemes by import, but don't expose this in the API. # Users should import specific schemes explicitly. from . import schemes as _schemes flufl.password-1.2.1/flufl/password/_generate.py0000664000175000017500000000357511710373107022235 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """User-friendly password generation.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'generate', ] import random from itertools import chain, product from string import ascii_lowercase EMPTYSTRING = '' _vowels = tuple('aeiou') _consonants = tuple(c for c in ascii_lowercase if c not in _vowels) _syllables = tuple(x + y for (x, y) in chain(product(_vowels, _consonants), product(_consonants, _vowels))) def generate(length=8): """Make a random *user friendly* password. Such passwords are nominally easier to pronounce and thus remember. Their security in relationship to purely random passwords has not been determined. :param length: Minimum length in characters for the resulting password. The password will always be an even number of characters. The default is to create passwords of length 8. :type length: int :return: The user friendly password. :rtype: string """ syllables = [] while len(syllables) * 2 < length: syllables.append(random.choice(_syllables)) return EMPTYSTRING.join(syllables)[:length] flufl.password-1.2.1/flufl/password/conf.py0000664000175000017500000001536511710373107021231 0ustar barrybarry00000000000000# -*- coding: utf-8 -*- # # flufl.password documentation build configuration file, created by # sphinx-quickstart on Thu Jan 7 18:41:30 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from __future__ import print_function import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['../../_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'README' # General information about the project. project = 'flufl.password' copyright = '2004-2012, Barry A. Warsaw' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # from flufl.password import __version__ # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build', 'build', 'flufl.password.egg-info', 'distribute-0.6.10'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['../../_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'fluflpassworddoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('README.txt', 'fluflpassword.tex', 'flufl.password Documentation', 'Barry A. Warsaw', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True import errno def index_html(): cwd = os.getcwd() try: os.chdir('build/sphinx/html') try: os.symlink('README.html', 'index.html') except OSError as error: if error.errno != errno.EEXIST: raise print('index.html -> README.html') finally: os.chdir(cwd) import atexit atexit.register(index_html) flufl.password-1.2.1/flufl/password/README.rst0000664000175000017500000000443311710373107021413 0ustar barrybarry00000000000000================================================== flufl.password - Password hashing and verification ================================================== This package is called ``flufl.password``. It provides for hashing and verification of passwords. Requirements ============ ``flufl.password`` requires Python 2.6 or newer, and is compatible with Python 3. Documentation ============= A `simple guide`_ to using the library is available within this package, in the form of doctests. The manual is also available online in the Cheeseshop at: http://package.python.org/flufl.password Project details =============== The project home page is: http://launchpad.net/flufl.password You should report bugs at: http://bugs.launchpad.net/flufl.password You can download the latest version of the package either from the Cheeseshop: http://pypi.python.org/pypi/flufl.password or from the Launchpad page above. Of course you can also just install it with ``pip`` or ``easy_install`` from the command line:: % sudo pip flufl.password % sudo easy_install flufl.password You can grab the latest development copy of the code using Bazaar, from the Launchpad home page above. See http://bazaar-vcs.org for details on the Bazaar distributed revision control system. If you have Bazaar installed, you can grab your own branch of the code like this:: bzr branch lp:flufl.password You may contact the author via barry@python.org. Copyright ========= Copyright (C) 2011-2012 Barry A. Warsaw This file is part of flufl.password. flufl.password is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. flufl.password is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with flufl.password. If not, see . Table of Contents ================= .. toctree:: docs/using.rst NEWS.rst .. _`simple guide`: docs/using.html flufl.password-1.2.1/flufl/password/tests/0000775000175000017500000000000011744121150021056 5ustar barrybarry00000000000000flufl.password-1.2.1/flufl/password/tests/__init__.py0000664000175000017500000000000011707351173023166 0ustar barrybarry00000000000000flufl.password-1.2.1/flufl/password/tests/test_passwords.py0000664000175000017500000001004011710373107024513 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Unit tests for the passwords module.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ ] import unittest # Python 3 does not have the izip form. try: from itertools import izip_longest as longest except ImportError: from itertools import zip_longest as longest from flufl.password._hash import make_secret from flufl.password._registry import BadPasswordSchemeError, lookup from flufl.password._verify import verify from flufl.password import schemes, generate class PasswordsTestBase: scheme = None def setUp(self): # The user's password, as a bytes self.pwbyte = b'abc' self.pwutf8 = 'abc\xc3\xbf' # 'abc\xff' # Bad passwords; bytes self.badbyte = b'def' def test_byte_passwords(self): secret = make_secret(self.pwbyte, self.scheme) self.assertTrue(verify(secret, self.pwbyte), self.scheme) self.assertFalse(verify(secret, self.badbyte), self.scheme) def test_utf8_passwords(self): secret = make_secret(self.pwutf8, self.scheme) self.assertTrue(verify(secret, self.pwutf8), self.scheme) self.assertFalse(verify(secret, self.badbyte), self.scheme) class TestNoPasswords(unittest.TestCase): def test_make_secret(self): self.assertEqual(schemes.NoPasswordScheme.make_secret('whatever'), b'') def test_check_response(self): self.assertFalse( schemes.NoPasswordScheme.check_response(b'foo', 'bar')) self.assertFalse(schemes.NoPasswordScheme.check_response(b'', 'bar')) class TestCleartextPasswords(PasswordsTestBase, unittest.TestCase): scheme = schemes.ClearTextPasswordScheme class TestSHAPasswords(PasswordsTestBase, unittest.TestCase): scheme = schemes.SHAPasswordScheme class TestSSHAPasswords(PasswordsTestBase, unittest.TestCase): scheme = schemes.SSHAPasswordScheme class TestPBKDF2Passwords(PasswordsTestBase, unittest.TestCase): scheme = schemes.PBKDF2PasswordScheme class TestSchemeLookup(unittest.TestCase): def test_scheme_name_lookup(self): self.assertEqual(lookup('NONE'), schemes.NoPasswordScheme) self.assertEqual(lookup('CLEARTEXT'), schemes.ClearTextPasswordScheme) self.assertEqual(lookup('SHA'), schemes.SHAPasswordScheme) self.assertEqual(lookup('SSHA'), schemes.SSHAPasswordScheme) self.assertEqual(lookup('PBKDF2'), schemes.PBKDF2PasswordScheme) def test_lookup_error(self): self.assertRaises(BadPasswordSchemeError, lookup, 'BOGUS') # See itertools doc page examples. def _grouper(seq): args = [iter(seq)] * 2 return list(longest(*args)) class TestPasswordGeneration(unittest.TestCase): def test_provided_user_friendly_password_length(self): self.assertEqual(len(generate(12)), 12) def test_provided_odd_user_friendly_password_length(self): self.assertEqual(len(generate(15)), 15) def test_user_friendly_password(self): password = generate() for pair in _grouper(password): # There will always be one vowel and one non-vowel. vowel = (pair[0] if pair[0] in 'aeiou' else pair[1]) consonant = (pair[0] if pair[0] not in 'aeiou' else pair[1]) self.assertTrue(vowel in 'aeiou', vowel) self.assertTrue(consonant not in 'aeiou', consonant) flufl.password-1.2.1/flufl/password/tests/test_documentation.py0000664000175000017500000000627411710373107025355 0ustar barrybarry00000000000000# Copyright (C) 2004-2012 by Barry A. Warsaw # # This file is part of flufl.password. # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Test harness for doctests.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'additional_tests', ] import os import atexit import doctest import unittest from pkg_resources import ( resource_filename, resource_exists, resource_listdir, cleanup_resources) COMMASPACE = ', ' DOT = '.' DOCTEST_FLAGS = ( doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF) def stop(): """Call into pdb.set_trace()""" # Do the import here so that you get the wacky special hacked pdb instead # of Python's normal pdb. import pdb pdb.set_trace() def show(thing): # Python 2 and 3 display bytes objects differently, even with print. This # makes it more difficult to write doctests that work in both versions of # Python, because hashed passwords are all bytes. The root cause is that # in Python 2, `bytes` is just an alias for `str` and in Python 3, the # repr of bytes includes the b'' prefix. Use this instead of print() in # the doctests. if isinstance(thing, bytes) and (bytes is not str): # There are lots of ways we could do this. thing = repr(thing)[2:-1] print(thing) def setup(testobj): """Test setup.""" # Make sure future statements in our doctests match the Python code. When # run with 2to3, the future import gets removed and these names are not # defined. try: testobj.globs['absolute_import'] = absolute_import testobj.globs['print_function'] = print_function testobj.globs['unicode_literals'] = unicode_literals except NameError: pass testobj.globs['show'] = show testobj.globs['stop'] = stop def additional_tests(): "Run the doc tests (README.rst and docs/*, if any exist)" doctest_files = [ os.path.abspath(resource_filename('flufl.password', 'README.rst'))] if resource_exists('flufl.password', 'docs'): for name in resource_listdir('flufl.password', 'docs'): if name.endswith('.rst'): doctest_files.append( os.path.abspath( resource_filename('flufl.password', 'docs/%s' % name))) kwargs = dict(module_relative=False, optionflags=DOCTEST_FLAGS, setUp=setup, ) atexit.register(cleanup_resources) return unittest.TestSuite(( doctest.DocFileSuite(*doctest_files, **kwargs))) flufl.password-1.2.1/flufl/password/_hash.py0000664000175000017500000000336111710373107021357 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Make secrets.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'make_secret', ] def make_secret(password, scheme=None): """Return a hashed password using the given scheme. :param password: The plain text password to hash. :type password: bytes or utf-8 encoded string. :param scheme: The scheme class to use for encryption. If not given, `SSHAPasswordScheme` is used. :type scheme: PasswordScheme subclass. :return: The hashed password. :rtype: bytes """ if not isinstance(password, bytes): # If it's not a bytes, it better be a unicode in Python 2 or a str in # Python 3. Let encoding errors propagate up. password = password.encode('utf-8') if scheme is None: from flufl.password.schemes import SSHAPasswordScheme scheme = SSHAPasswordScheme secret = scheme.make_secret(password) if not isinstance(scheme.TAG, bytes): tag = scheme.TAG.encode('utf-8') return b'{' + tag + b'}' + secret flufl.password-1.2.1/flufl/password/schemes.py0000664000175000017500000001564411710373107021733 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Password hashing and verification schemes. Represents passwords using RFC 2307 syntax (as best we can tell). """ from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'ClearTextPasswordScheme', 'NoPasswordScheme', 'PBKDF2PasswordScheme', 'PasswordScheme', 'SHAPasswordScheme', 'SSHAPasswordScheme', ] import os import hmac import hashlib from array import array from base64 import urlsafe_b64decode as decode from base64 import urlsafe_b64encode as encode from functools import partial from flufl.password._registry import register from flufl.password._utils import BadPasswordFormatError SALT_LENGTH = 20 # bytes ITERATIONS = 2000 class PasswordScheme: """Password scheme base class.""" TAG = '' @staticmethod def make_secret(password): """Return the hashed password. :param password: The clear text password. :type password: bytes or utf-8 encoded string. :return: The encrypted password. :rtype: bytes """ raise NotImplementedError @classmethod def check_response(cls, hashed, cleartext, scheme=None): """Check a hashed password against a clear text response. :param hashed: The user's stored hashed password, without the scheme prefix (i.e. strip off the *{SCHEME}* part first). :type hashed: bytes :param cleartext: The clear text password entered by the user for authentication. :type cleartext: bytes or utf-8 encoded string. :param scheme: The scheme tag i.e. the *{SCHEME}* part without the braces. This can be omitted when called on the class directly, unless the scheme puts some additional information in there. It is always provided by the `verify()` API. :type response: bytes or utf-8 encoded string. :return: True if the clear text matches the hash :rtype: bool """ return hashed == cls.make_secret(cleartext) @register class NoPasswordScheme(PasswordScheme): """A password scheme without passwords.""" TAG = 'NONE' @staticmethod def make_secret(password): """See `PasswordScheme`.""" return b'' @classmethod def check_response(cls, hashed, cleartext, scheme=None): """See `PasswordScheme`.""" return False @register class ClearTextPasswordScheme(PasswordScheme): """A password scheme that stores clear text passwords.""" TAG = 'CLEARTEXT' @staticmethod def make_secret(password): """See `PasswordScheme`.""" return password @register class SHAPasswordScheme(PasswordScheme): """A password scheme that encodes the password using SHA1.""" TAG = 'SHA' @staticmethod def make_secret(password): """See `PasswordScheme`.""" h = hashlib.sha1(password) return encode(h.digest()) @register class SSHAPasswordScheme(PasswordScheme): """A password scheme that encodes the password using salted SHA1.""" TAG = 'SSHA' @staticmethod def _make_salted_hash(password, salt): h = hashlib.sha1(password) h.update(salt) return encode(h.digest() + salt) @staticmethod def make_secret(password): """See `PasswordScheme`.""" salt = os.urandom(SALT_LENGTH) return SSHAPasswordScheme._make_salted_hash(password, salt) @classmethod def check_response(cls, hashed, cleartext, scheme=None): """See `PasswordScheme`.""" # The salt is always 20 bytes and always tacked onto the end. decoded = decode(hashed) salt = decoded[20:] return hashed == cls._make_salted_hash(cleartext, salt) def _bytes_of(array_obj): # Avoid DeprecationWarnings in Python 3. try: return array_obj.tobytes() except AttributeError: return array_obj.tostring() # Basic algorithm given by Bob Fleck @register class PBKDF2PasswordScheme(PasswordScheme): """RFC 2989 password encoding scheme.""" # This is a bit nasty if we wanted a different prf or iterations. OTOH, # we really have no clue what the standard LDAP-ish specification for # those options is. TAG = 'PBKDF2 SHA {0}'.format(ITERATIONS) @staticmethod def _pbkdf2(password, salt, iterations): """From RFC2898 sec. 5.2. Simplified to handle only 20 byte output case. Output of 20 bytes means always exactly one block to handle, and a constant block counter appended to the salt in the initial hmac update. """ # We do it this way because the array() constructor takes a 'native # string'. You can't use unicodes in Python 2 or bytes in Python 3. array_of_signedints = partial(array, str('i')) h = hmac.new(password, None, hashlib.sha1) prf = h.copy() prf.update(salt + b'\x00\x00\x00\x01') T = U = array_of_signedints(prf.digest()) while iterations: prf = h.copy() prf.update(_bytes_of(U)) U = array_of_signedints(prf.digest()) T = array_of_signedints((t ^ u for t, u in zip(T, U))) iterations -= 1 return _bytes_of(T) @staticmethod def make_secret(password): """See `PasswordScheme`. From RFC2898 sec. 5.2. Simplified to handle only 20 byte output case. Output of 20 bytes means always exactly one block to handle, and a constant block counter appended to the salt in the initial hmac update. """ salt = os.urandom(SALT_LENGTH) digest = PBKDF2PasswordScheme._pbkdf2(password, salt, ITERATIONS) return encode(digest + salt) @classmethod def check_response(cls, hashed, cleartext, scheme): """See `PasswordScheme`.""" parts = scheme.split() if (len(parts) != 3 or parts[0].upper() != 'PBKDF2' or parts[1].upper() != 'SHA' ): raise BadPasswordFormatError(scheme) try: iterations = int(parts[2]) except ValueError: raise BadPasswordFormatError(scheme) decoded = decode(hashed) salt = decoded[20:] secret = decoded[:20] return secret == cls._pbkdf2(cleartext, salt, iterations) flufl.password-1.2.1/distribute_setup.py0000664000175000017500000003661511707351173020736 0ustar barrybarry00000000000000#!python """Bootstrap distribute installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from distribute_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import os import sys import time import fnmatch import tempfile import tarfile from distutils import log try: from site import USER_SITE except ImportError: USER_SITE = None try: import subprocess def _python_cmd(*args): args = (sys.executable,) + args return subprocess.call(args) == 0 except ImportError: # will be used for python 2.3 def _python_cmd(*args): args = (sys.executable,) + args # quoting arguments if windows if sys.platform == 'win32': def quote(arg): if ' ' in arg: return '"%s"' % arg return arg args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 DEFAULT_VERSION = "0.6.19" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" SETUPTOOLS_PKG_INFO = """\ Metadata-Version: 1.0 Name: setuptools Version: %s Summary: xxxx Home-page: xxx Author: xxx Author-email: xxx License: xxx Description: xxx """ % SETUPTOOLS_FAKED_VERSION def _install(tarball): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # installing log.warn('Installing Distribute') if not _python_cmd('setup.py', 'install'): log.warn('Something went wrong during the installation.') log.warn('See the error message above.') finally: os.chdir(old_wd) def _build_egg(egg, tarball, to_dir): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # building an egg log.warn('Building a Distribute egg in %s', to_dir) _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) finally: os.chdir(old_wd) # returning the result log.warn(egg) if not os.path.exists(egg): raise IOError('Could not build the egg.') def _do_download(version, download_base, to_dir, download_delay): egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' % (version, sys.version_info[0], sys.version_info[1])) if not os.path.exists(egg): tarball = download_setuptools(version, download_base, to_dir, download_delay) _build_egg(egg, tarball, to_dir) sys.path.insert(0, egg) import setuptools setuptools.bootstrap_install_from = egg def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15, no_fake=True): # making sure we use the absolute path to_dir = os.path.abspath(to_dir) was_imported = 'pkg_resources' in sys.modules or \ 'setuptools' in sys.modules try: try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): if not no_fake: _fake_setuptools() raise ImportError except ImportError: return _do_download(version, download_base, to_dir, download_delay) try: pkg_resources.require("distribute>="+version) return except pkg_resources.VersionConflict: e = sys.exc_info()[1] if was_imported: sys.stderr.write( "The required version of distribute (>=%s) is not available,\n" "and can't be installed while this script is running. Please\n" "install a more recent version first, using\n" "'easy_install -U distribute'." "\n\n(Currently using %r)\n" % (version, e.args[0])) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return _do_download(version, download_base, to_dir, download_delay) except pkg_resources.DistributionNotFound: return _do_download(version, download_base, to_dir, download_delay) finally: if not no_fake: _create_fake_setuptools_pkg_info(to_dir) def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay=15): """Download distribute from a specified location and return its filename `version` should be a valid distribute version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ # making sure we use the absolute path to_dir = os.path.abspath(to_dir) try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen tgz_name = "distribute-%s.tar.gz" % version url = download_base + tgz_name saveto = os.path.join(to_dir, tgz_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: log.warn("Downloading %s", url) src = urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = src.read() dst = open(saveto, "wb") dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def _no_sandbox(function): def __no_sandbox(*args, **kw): try: from setuptools.sandbox import DirectorySandbox if not hasattr(DirectorySandbox, '_old'): def violation(*args): pass DirectorySandbox._old = DirectorySandbox._violation DirectorySandbox._violation = violation patched = True else: patched = False except ImportError: patched = False try: return function(*args, **kw) finally: if patched: DirectorySandbox._violation = DirectorySandbox._old del DirectorySandbox._old return __no_sandbox def _patch_file(path, content): """Will backup the file then patch it""" existing_content = open(path).read() if existing_content == content: # already patched log.warn('Already patched.') return False log.warn('Patching...') _rename_path(path) f = open(path, 'w') try: f.write(content) finally: f.close() return True _patch_file = _no_sandbox(_patch_file) def _same_content(path, content): return open(path).read() == content def _rename_path(path): new_name = path + '.OLD.%s' % time.time() log.warn('Renaming %s into %s', path, new_name) os.rename(path, new_name) return new_name def _remove_flat_installation(placeholder): if not os.path.isdir(placeholder): log.warn('Unkown installation at %s', placeholder) return False found = False for file in os.listdir(placeholder): if fnmatch.fnmatch(file, 'setuptools*.egg-info'): found = True break if not found: log.warn('Could not locate setuptools*.egg-info') return log.warn('Removing elements out of the way...') pkg_info = os.path.join(placeholder, file) if os.path.isdir(pkg_info): patched = _patch_egg_dir(pkg_info) else: patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) if not patched: log.warn('%s already patched.', pkg_info) return False # now let's move the files out of the way for element in ('setuptools', 'pkg_resources.py', 'site.py'): element = os.path.join(placeholder, element) if os.path.exists(element): _rename_path(element) else: log.warn('Could not find the %s element of the ' 'Setuptools distribution', element) return True _remove_flat_installation = _no_sandbox(_remove_flat_installation) def _after_install(dist): log.warn('After install bootstrap.') placeholder = dist.get_command_obj('install').install_purelib _create_fake_setuptools_pkg_info(placeholder) def _create_fake_setuptools_pkg_info(placeholder): if not placeholder or not os.path.exists(placeholder): log.warn('Could not find the install location') return pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) setuptools_file = 'setuptools-%s-py%s.egg-info' % \ (SETUPTOOLS_FAKED_VERSION, pyver) pkg_info = os.path.join(placeholder, setuptools_file) if os.path.exists(pkg_info): log.warn('%s already exists', pkg_info) return log.warn('Creating %s', pkg_info) f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() pth_file = os.path.join(placeholder, 'setuptools.pth') log.warn('Creating %s', pth_file) f = open(pth_file, 'w') try: f.write(os.path.join(os.curdir, setuptools_file)) finally: f.close() _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) def _patch_egg_dir(path): # let's check if it's already patched pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') if os.path.exists(pkg_info): if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): log.warn('%s already patched.', pkg_info) return False _rename_path(path) os.mkdir(path) os.mkdir(os.path.join(path, 'EGG-INFO')) pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() return True _patch_egg_dir = _no_sandbox(_patch_egg_dir) def _before_install(): log.warn('Before install bootstrap.') _fake_setuptools() def _under_prefix(location): if 'install' not in sys.argv: return True args = sys.argv[sys.argv.index('install')+1:] for index, arg in enumerate(args): for option in ('--root', '--prefix'): if arg.startswith('%s=' % option): top_dir = arg.split('root=')[-1] return location.startswith(top_dir) elif arg == option: if len(args) > index: top_dir = args[index+1] return location.startswith(top_dir) if arg == '--user' and USER_SITE is not None: return location.startswith(USER_SITE) return True def _fake_setuptools(): log.warn('Scanning installed packages') try: import pkg_resources except ImportError: # we're cool log.warn('Setuptools or Distribute does not seem to be installed.') return ws = pkg_resources.working_set try: setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', replacement=False)) except TypeError: # old distribute API setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) if setuptools_dist is None: log.warn('No setuptools distribution found') return # detecting if it was already faked setuptools_location = setuptools_dist.location log.warn('Setuptools installation detected at %s', setuptools_location) # if --root or --preix was provided, and if # setuptools is not located in them, we don't patch it if not _under_prefix(setuptools_location): log.warn('Not patching, --root or --prefix is installing Distribute' ' in another location') return # let's see if its an egg if not setuptools_location.endswith('.egg'): log.warn('Non-egg installation') res = _remove_flat_installation(setuptools_location) if not res: return else: log.warn('Egg installation') pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') if (os.path.exists(pkg_info) and _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): log.warn('Already patched.') return log.warn('Patching...') # let's create a fake egg replacing setuptools one res = _patch_egg_dir(setuptools_location) if not res: return log.warn('Patched done.') _relaunch() def _relaunch(): log.warn('Relaunching...') # we have to relaunch the process # pip marker to avoid a relaunch bug if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: sys.argv[0] = 'setup.py' args = [sys.executable] + sys.argv sys.exit(subprocess.call(args)) def _extractall(self, path=".", members=None): """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory to extract to. `members' is optional and must be a subset of the list returned by getmembers(). """ import copy import operator from tarfile import ExtractError directories = [] if members is None: members = self for tarinfo in members: if tarinfo.isdir(): # Extract directories with a safe mode. directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 448 # decimal for oct 0700 self.extract(tarinfo, path) # Reverse sort directories. if sys.version_info < (2, 4): def sorter(dir1, dir2): return cmp(dir1.name, dir2.name) directories.sort(sorter) directories.reverse() else: directories.sort(key=operator.attrgetter('name'), reverse=True) # Set correct owner, mtime and filemode on directories. for tarinfo in directories: dirpath = os.path.join(path, tarinfo.name) try: self.chown(tarinfo, dirpath) self.utime(tarinfo, dirpath) self.chmod(tarinfo, dirpath) except ExtractError: e = sys.exc_info()[1] if self.errorlevel > 1: raise else: self._dbg(1, "tarfile: %s" % e) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" tarball = download_setuptools() _install(tarball) if __name__ == '__main__': main(sys.argv[1:]) flufl.password-1.2.1/flufl.password.egg-info/0000775000175000017500000000000011744121150021405 5ustar barrybarry00000000000000flufl.password-1.2.1/flufl.password.egg-info/SOURCES.txt0000664000175000017500000000134511744121150023274 0ustar barrybarry00000000000000MANIFEST.in README.rst distribute_setup.py setup.cfg setup.py setup_helpers.py template.py flufl/__init__.py flufl.password.egg-info/PKG-INFO flufl.password.egg-info/SOURCES.txt flufl.password.egg-info/dependency_links.txt flufl.password.egg-info/namespace_packages.txt flufl.password.egg-info/top_level.txt flufl/password/NEWS.rst flufl/password/README.rst flufl/password/__init__.py flufl/password/_generate.py flufl/password/_hash.py flufl/password/_registry.py flufl/password/_utils.py flufl/password/_verify.py flufl/password/conf.py flufl/password/schemes.py flufl/password/docs/__init__.py flufl/password/docs/using.rst flufl/password/tests/__init__.py flufl/password/tests/test_documentation.py flufl/password/tests/test_passwords.pyflufl.password-1.2.1/flufl.password.egg-info/top_level.txt0000664000175000017500000000000611744121150024133 0ustar barrybarry00000000000000flufl flufl.password-1.2.1/flufl.password.egg-info/dependency_links.txt0000664000175000017500000000000111744121150025453 0ustar barrybarry00000000000000 flufl.password-1.2.1/flufl.password.egg-info/namespace_packages.txt0000664000175000017500000000000611744121150025734 0ustar barrybarry00000000000000flufl flufl.password-1.2.1/flufl.password.egg-info/PKG-INFO0000664000175000017500000000626111744121150022507 0ustar barrybarry00000000000000Metadata-Version: 1.1 Name: flufl.password Version: 1.2.1 Summary: Password hashing and verification. Home-page: http://launchpad.net/flufl.password Author: Barry Warsaw Author-email: barry@python.org License: LGPLv3 Download-URL: https://launchpad.net/flufl.password/+download Description: ============== flufl.password ============== Password hashing and verification. The ``flufl.password`` library provides hashing and verification of passwords. License ======= This file is part of flufl.password. flufl.password is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 3 of the License. flufl.password is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with flufl.password. If not, see . ======================= NEWS for flufl.password ======================= 1.2.1 (2012-04-19) ================== * Add classifiers to setup.py and make the long description more compatible with the Cheeseshop. * Other changes to make the Cheeseshop page look nicer. (LP: #680136) * setup_helper.py version 2.1. 1.2 (2012-01-23) ================ * Fix some packaging issues. * Remove tox.ini. * Bump Copyright years. * Update standard template. * Eliminate the need to use 2to3, and fix some Python 3 deprecations. 1.1.1 (2012-01-01) ================== * Ensure all built-in schemes are registered by importing them in the __init__.py file. 1.1 (2011-12-31) ================ * Add user-friendly password generation API. 1.0 (2011-12-31) ================ * Initial release. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS :: MacOS X Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules flufl.password-1.2.1/PKG-INFO0000664000175000017500000000626111744121150016044 0ustar barrybarry00000000000000Metadata-Version: 1.1 Name: flufl.password Version: 1.2.1 Summary: Password hashing and verification. Home-page: http://launchpad.net/flufl.password Author: Barry Warsaw Author-email: barry@python.org License: LGPLv3 Download-URL: https://launchpad.net/flufl.password/+download Description: ============== flufl.password ============== Password hashing and verification. The ``flufl.password`` library provides hashing and verification of passwords. License ======= This file is part of flufl.password. flufl.password is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 3 of the License. flufl.password is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with flufl.password. If not, see . ======================= NEWS for flufl.password ======================= 1.2.1 (2012-04-19) ================== * Add classifiers to setup.py and make the long description more compatible with the Cheeseshop. * Other changes to make the Cheeseshop page look nicer. (LP: #680136) * setup_helper.py version 2.1. 1.2 (2012-01-23) ================ * Fix some packaging issues. * Remove tox.ini. * Bump Copyright years. * Update standard template. * Eliminate the need to use 2to3, and fix some Python 3 deprecations. 1.1.1 (2012-01-01) ================== * Ensure all built-in schemes are registered by importing them in the __init__.py file. 1.1 (2011-12-31) ================ * Add user-friendly password generation API. 1.0 (2011-12-31) ================ * Initial release. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS :: MacOS X Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules flufl.password-1.2.1/template.py0000664000175000017500000000150611710373107017135 0ustar barrybarry00000000000000# Copyright (C) 2011-2012 by Barry A. Warsaw # # This file is part of flufl.password # # flufl.password is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, version 3 of the License. # # flufl.password is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with flufl.password. If not, see . """Module contents.""" from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ ] flufl.password-1.2.1/README.rst0000664000175000017500000000145311744121025016435 0ustar barrybarry00000000000000============== flufl.password ============== Password hashing and verification. The ``flufl.password`` library provides hashing and verification of passwords. License ======= This file is part of flufl.password. flufl.password is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 3 of the License. flufl.password is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with flufl.password. If not, see .