flufl.password-1.2.1/ 0000775 0001750 0001750 00000000000 11744121150 014742 5 ustar barry barry 0000000 0000000 flufl.password-1.2.1/MANIFEST.in 0000664 0001750 0001750 00000000064 11707351173 016511 0 ustar barry barry 0000000 0000000 include *.py
global-include *.txt *.rst
prune build
flufl.password-1.2.1/setup.py 0000664 0001750 0001750 00000004306 11744120742 016465 0 ustar barry barry 0000000 0000000 # 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.cfg 0000664 0001750 0001750 00000000225 11744121150 016562 0 ustar barry barry 0000000 0000000 [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.py 0000664 0001750 0001750 00000012062 11744120710 020200 0 ustar barry barry 0000000 0000000 # 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/ 0000775 0001750 0001750 00000000000 11744121150 016052 5 ustar barry barry 0000000 0000000 flufl.password-1.2.1/flufl/__init__.py 0000664 0001750 0001750 00000001606 11707351173 020177 0 ustar barry barry 0000000 0000000 # 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/ 0000775 0001750 0001750 00000000000 11744121150 017714 5 ustar barry barry 0000000 0000000 flufl.password-1.2.1/flufl/password/_registry.py 0000664 0001750 0001750 00000004027 11710373107 022304 0 ustar barry barry 0000000 0000000 # 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/ 0000775 0001750 0001750 00000000000 11744121150 020644 5 ustar barry barry 0000000 0000000 flufl.password-1.2.1/flufl/password/docs/using.rst 0000664 0001750 0001750 00000012725 11707351173 022543 0 ustar barry barry 0000000 0000000 ================================
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__.py 0000664 0001750 0001750 00000000000 11707351173 022754 0 ustar barry barry 0000000 0000000 flufl.password-1.2.1/flufl/password/_verify.py 0000664 0001750 0001750 00000003315 11710373107 021737 0 ustar barry barry 0000000 0000000 # 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.py 0000664 0001750 0001750 00000002367 11710373107 021601 0 ustar barry barry 0000000 0000000 # 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.rst 0000664 0001750 0001750 00000001434 11744120652 021232 0 ustar barry barry 0000000 0000000 =======================
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__.py 0000664 0001750 0001750 00000002544 11744120661 022040 0 ustar barry barry 0000000 0000000 # 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.py 0000664 0001750 0001750 00000003575 11710373107 022235 0 ustar barry barry 0000000 0000000 # 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.py 0000664 0001750 0001750 00000015365 11710373107 021231 0 ustar barry barry 0000000 0000000 # -*- 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.rst 0000664 0001750 0001750 00000004433 11710373107 021413 0 ustar barry barry 0000000 0000000 ==================================================
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/ 0000775 0001750 0001750 00000000000 11744121150 021056 5 ustar barry barry 0000000 0000000 flufl.password-1.2.1/flufl/password/tests/__init__.py 0000664 0001750 0001750 00000000000 11707351173 023166 0 ustar barry barry 0000000 0000000 flufl.password-1.2.1/flufl/password/tests/test_passwords.py 0000664 0001750 0001750 00000010040 11710373107 024513 0 ustar barry barry 0000000 0000000 # 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.py 0000664 0001750 0001750 00000006274 11710373107 025355 0 ustar barry barry 0000000 0000000 # 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.py 0000664 0001750 0001750 00000003361 11710373107 021357 0 ustar barry barry 0000000 0000000 # 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.py 0000664 0001750 0001750 00000015644 11710373107 021733 0 ustar barry barry 0000000 0000000 # 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.py 0000664 0001750 0001750 00000036615 11707351173 020736 0 ustar barry barry 0000000 0000000 #!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/ 0000775 0001750 0001750 00000000000 11744121150 021405 5 ustar barry barry 0000000 0000000 flufl.password-1.2.1/flufl.password.egg-info/SOURCES.txt 0000664 0001750 0001750 00000001345 11744121150 023274 0 ustar barry barry 0000000 0000000 MANIFEST.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.py flufl.password-1.2.1/flufl.password.egg-info/top_level.txt 0000664 0001750 0001750 00000000006 11744121150 024133 0 ustar barry barry 0000000 0000000 flufl
flufl.password-1.2.1/flufl.password.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 11744121150 025453 0 ustar barry barry 0000000 0000000
flufl.password-1.2.1/flufl.password.egg-info/namespace_packages.txt 0000664 0001750 0001750 00000000006 11744121150 025734 0 ustar barry barry 0000000 0000000 flufl
flufl.password-1.2.1/flufl.password.egg-info/PKG-INFO 0000664 0001750 0001750 00000006261 11744121150 022507 0 ustar barry barry 0000000 0000000 Metadata-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-INFO 0000664 0001750 0001750 00000006261 11744121150 016044 0 ustar barry barry 0000000 0000000 Metadata-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.py 0000664 0001750 0001750 00000001506 11710373107 017135 0 ustar barry barry 0000000 0000000 # 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.rst 0000664 0001750 0001750 00000001453 11744121025 016435 0 ustar barry barry 0000000 0000000 ==============
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 .