flufl.password-1.3/ 0000775 0001750 0001750 00000000000 12410633530 014605 5 ustar barry barry 0000000 0000000 flufl.password-1.3/PKG-INFO 0000664 0001750 0001750 00000006652 12410633530 015713 0 ustar barry barry 0000000 0000000 Metadata-Version: 1.1
Name: flufl.password
Version: 1.3
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.3 (2014-09-24)
================
* Fix documentation bug. (LP: #1026403)
* Purge all references to `distribute`.
* Describe the switch to git and the repository move.
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.3/setup.py 0000664 0001750 0001750 00000004213 12410627537 016331 0 ustar barry barry 0000000 0000000 # Copyright (C) 2004-2014 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 .
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.3/flufl/ 0000775 0001750 0001750 00000000000 12410633530 015715 5 ustar barry barry 0000000 0000000 flufl.password-1.3/flufl/password/ 0000775 0001750 0001750 00000000000 12410633530 017557 5 ustar barry barry 0000000 0000000 flufl.password-1.3/flufl/password/schemes.py 0000664 0001750 0001750 00000015644 12410627540 021576 0 ustar barry barry 0000000 0000000 # Copyright (C) 2011-2014 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.3/flufl/password/_generate.py 0000664 0001750 0001750 00000003575 12410627540 022100 0 ustar barry barry 0000000 0000000 # Copyright (C) 2011-2014 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.3/flufl/password/_registry.py 0000664 0001750 0001750 00000004027 12410627540 022147 0 ustar barry barry 0000000 0000000 # Copyright (C) 2011-2014 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.3/flufl/password/__init__.py 0000664 0001750 0001750 00000002542 12410632701 021672 0 ustar barry barry 0000000 0000000 # Copyright (C) 2004-2014 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.3'
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.3/flufl/password/_utils.py 0000664 0001750 0001750 00000002367 12410627540 021444 0 ustar barry barry 0000000 0000000 # Copyright (C) 2011-2014 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.3/flufl/password/conf.py 0000664 0001750 0001750 00000015365 12410627565 021103 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-2014, 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.3/flufl/password/NEWS.rst 0000664 0001750 0001750 00000001712 12410632171 021066 0 ustar barry barry 0000000 0000000 =======================
NEWS for flufl.password
=======================
1.3 (2014-09-24)
================
* Fix documentation bug. (LP: #1026403)
* Purge all references to `distribute`.
* Describe the switch to git and the repository move.
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.3/flufl/password/_hash.py 0000664 0001750 0001750 00000003360 12410630115 021211 0 ustar barry barry 0000000 0000000 # Copyright (C) 2011-2014 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.3/flufl/password/README.rst 0000664 0001750 0001750 00000004561 12410630274 021256 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
Of course you can also just install it with ``pip``::
% sudo pip install flufl.password
You may want to use `virtualenv`_ instead of installing the package into the
system Python.
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::
$ git clone git://gitorious.org/flufl-password/flufl-password.git
You may contact the author via barry@python.org.
Copyright
=========
Copyright (C) 2011-2014 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
.. _`virtualenv`: http://www.virtualenv.org/en/latest/index.html
flufl.password-1.3/flufl/password/_verify.py 0000664 0001750 0001750 00000003315 12410627540 021602 0 ustar barry barry 0000000 0000000 # Copyright (C) 2011-2014 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.3/flufl/password/tests/ 0000775 0001750 0001750 00000000000 12410633530 020721 5 ustar barry barry 0000000 0000000 flufl.password-1.3/flufl/password/tests/test_documentation.py 0000664 0001750 0001750 00000006274 12410627540 025220 0 ustar barry barry 0000000 0000000 # Copyright (C) 2004-2014 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.3/flufl/password/tests/test_passwords.py 0000664 0001750 0001750 00000010327 12410630510 024355 0 ustar barry barry 0000000 0000000 # Copyright (C) 2011-2014 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__ = [
'TestCleartextPasswords',
'TestNoPasswords',
'TestPBKDF2Passwords',
'TestPasswordGeneration',
'TestSHAPasswords',
'TestSSHAPasswords',
'TestSchemeLookup',
]
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.3/flufl/password/tests/__init__.py 0000664 0001750 0001750 00000000000 12410627540 023024 0 ustar barry barry 0000000 0000000 flufl.password-1.3/flufl/password/docs/ 0000775 0001750 0001750 00000000000 12410633530 020507 5 ustar barry barry 0000000 0000000 flufl.password-1.3/flufl/password/docs/using.rst 0000664 0001750 0001750 00000012725 12410627540 022401 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.3/flufl/password/docs/__init__.py 0000664 0001750 0001750 00000000000 12410627540 022612 0 ustar barry barry 0000000 0000000 flufl.password-1.3/flufl/__init__.py 0000664 0001750 0001750 00000001606 12410627540 020035 0 ustar barry barry 0000000 0000000 # Copyright (C) 2004-2014 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.3/setup.cfg 0000664 0001750 0001750 00000000225 12410633530 016425 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.3/template.py 0000664 0001750 0001750 00000001501 12410630050 016761 0 ustar barry barry 0000000 0000000 # Copyright (C) 2014 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.3/MANIFEST.in 0000664 0001750 0001750 00000000064 12410627537 016355 0 ustar barry barry 0000000 0000000 include *.py
global-include *.txt *.rst
prune build
flufl.password-1.3/README.rst 0000664 0001750 0001750 00000001453 12410627537 016311 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 .
flufl.password-1.3/flufl.password.egg-info/ 0000775 0001750 0001750 00000000000 12410633530 021250 5 ustar barry barry 0000000 0000000 flufl.password-1.3/flufl.password.egg-info/PKG-INFO 0000664 0001750 0001750 00000006652 12410633530 022356 0 ustar barry barry 0000000 0000000 Metadata-Version: 1.1
Name: flufl.password
Version: 1.3
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.3 (2014-09-24)
================
* Fix documentation bug. (LP: #1026403)
* Purge all references to `distribute`.
* Describe the switch to git and the repository move.
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.3/flufl.password.egg-info/SOURCES.txt 0000664 0001750 0001750 00000001321 12410633530 023131 0 ustar barry barry 0000000 0000000 MANIFEST.in
README.rst
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.3/flufl.password.egg-info/top_level.txt 0000664 0001750 0001750 00000000006 12410633530 023776 0 ustar barry barry 0000000 0000000 flufl
flufl.password-1.3/flufl.password.egg-info/namespace_packages.txt 0000664 0001750 0001750 00000000006 12410633530 025577 0 ustar barry barry 0000000 0000000 flufl
flufl.password-1.3/flufl.password.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 12410633530 025316 0 ustar barry barry 0000000 0000000
flufl.password-1.3/setup_helpers.py 0000664 0001750 0001750 00000012062 12410627537 020054 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2014 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