[Python-modules-commits] [python-django-otp] 02/13: importing python-django-otp_0.3.8.orig.tar.gz
Michael Fladischer
fladi at moszumanska.debian.org
Wed Mar 1 08:40:43 UTC 2017
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch master
in repository python-django-otp.
commit 005ed8efc70304aec1ed83ead75ebe646e4528f2
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Sun Feb 26 16:40:31 2017 +0100
importing python-django-otp_0.3.8.orig.tar.gz
---
CHANGES | 185 +++++++++++
LICENSE | 23 ++
MANIFEST.in | 7 +
PKG-INFO | 48 +++
README.rst | 25 ++
django_otp.egg-info/PKG-INFO | 48 +++
django_otp.egg-info/SOURCES.txt | 78 +++++
django_otp.egg-info/dependency_links.txt | 1 +
django_otp.egg-info/not-zip-safe | 1 +
django_otp.egg-info/requires.txt | 1 +
django_otp.egg-info/top_level.txt | 1 +
django_otp/__init__.py | 164 +++++++++
django_otp/admin.py | 85 +++++
django_otp/conf.py | 27 ++
django_otp/decorators.py | 27 ++
django_otp/forms.py | 250 ++++++++++++++
django_otp/middleware.py | 51 +++
django_otp/models.py | 159 +++++++++
django_otp/oath.py | 205 ++++++++++++
django_otp/plugins/__init__.py | 0
django_otp/plugins/otp_email/__init__.py | 0
django_otp/plugins/otp_email/admin.py | 29 ++
django_otp/plugins/otp_email/conf.py | 28 ++
.../plugins/otp_email/migrations/0001_initial.py | 30 ++
.../plugins/otp_email/migrations/__init__.py | 0
django_otp/plugins/otp_email/models.py | 66 ++++
.../otp_email/south_migrations/0001_initial.py | 74 +++++
.../plugins/otp_email/south_migrations/__init__.py | 0
.../otp_email/templates/otp/email/token.txt | 1 +
django_otp/plugins/otp_email/tests.py | 44 +++
django_otp/plugins/otp_hotp/__init__.py | 0
django_otp/plugins/otp_hotp/admin.py | 33 ++
.../plugins/otp_hotp/migrations/0001_initial.py | 34 ++
django_otp/plugins/otp_hotp/migrations/__init__.py | 0
django_otp/plugins/otp_hotp/models.py | 78 +++++
.../otp_hotp/south_migrations/0001_initial.py | 80 +++++
.../plugins/otp_hotp/south_migrations/__init__.py | 0
django_otp/plugins/otp_hotp/tests.py | 45 +++
django_otp/plugins/otp_static/__init__.py | 0
django_otp/plugins/otp_static/admin.py | 35 ++
django_otp/plugins/otp_static/lib.py | 29 ++
.../plugins/otp_static/management/__init__.py | 0
.../otp_static/management/commands/__init__.py | 0
.../management/commands/addstatictoken.py | 31 ++
.../plugins/otp_static/migrations/0001_initial.py | 39 +++
.../plugins/otp_static/migrations/__init__.py | 0
django_otp/plugins/otp_static/models.py | 58 ++++
.../otp_static/south_migrations/0001_initial.py | 100 ++++++
.../otp_static/south_migrations/__init__.py | 0
django_otp/plugins/otp_static/tests.py | 166 +++++++++
django_otp/plugins/otp_totp/__init__.py | 0
django_otp/plugins/otp_totp/admin.py | 33 ++
.../plugins/otp_totp/migrations/0001_initial.py | 37 +++
django_otp/plugins/otp_totp/migrations/__init__.py | 0
django_otp/plugins/otp_totp/models.py | 108 ++++++
.../otp_totp/south_migrations/0001_initial.py | 91 +++++
.../otp_totp/south_migrations/0002_last_t.py | 82 +++++
.../plugins/otp_totp/south_migrations/__init__.py | 0
django_otp/plugins/otp_totp/tests.py | 77 +++++
django_otp/templates/otp/.DS_Store | Bin 0 -> 6148 bytes
django_otp/templates/otp/admin14/login.html | 69 ++++
django_otp/templates/otp/admin15/login.html | 68 ++++
django_otp/templates/otp/admin16/login.html | 84 +++++
django_otp/templates/otp/admin17/login.html | 83 +++++
django_otp/templates/otp/admin18/login.html | 85 +++++
django_otp/templates/otp/admin19/login.html | 96 ++++++
django_otp/tests.py | 57 ++++
django_otp/util.py | 62 ++++
django_otp/views.py | 39 +++
docs/Makefile | 157 +++++++++
docs/ext/otpdocs.py | 10 +
docs/source/auth.rst | 181 ++++++++++
docs/source/changes.rst | 4 +
docs/source/conf.py | 267 +++++++++++++++
docs/source/extend.rst | 86 +++++
docs/source/index.rst | 40 +++
docs/source/overview.rst | 370 +++++++++++++++++++++
setup.cfg | 11 +
setup.py | 36 ++
79 files changed, 4619 insertions(+)
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..de303ef
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,185 @@
+v0.3.8 - November 27, 2016 - Forward compatbility for Django 2.0
+----------------------------------------------------------------
+
+- Treat :attr:`~django.contrib.auth.models.User.is_authenticated` and
+ :attr:`~django.contrib.auth.models.User.is_anonymous` as properties in Django
+ 1.10 and later.
+
+- Add explict on_delete behavior for all foreign keys.
+
+
+v0.3.7 - September 24, 2016 - Convenience API
+---------------------------------------------
+
+- Added a convenience API for verifying TOTP tokens:
+ :meth:`django_otp.oath.TOTP.verify`.
+
+
+v0.3.6 - September 4, 2016 - Django 1.10
+----------------------------------------
+
+- `#11`_: Don't break the laziness of ``request.user``.
+
+- `#16`_: Improved error message for invalid tokens.
+
+- `#17`_: Support the new middleware API in Django 1.10.
+
+.. _#11: https://bitbucket.org/psagers/django-otp/issues/11/wasteful-queries-on-every-request
+.. _#16: https://bitbucket.org/psagers/django-otp/issues/16/inappropriate-error-when-_verify_token
+.. _#17: https://bitbucket.org/psagers/django-otp/issues/17/django-110-new-style-middleware
+
+
+v0.3.5 - April 13, 2016 - Fix default TOTP key
+----------------------------------------------
+
+- The default (random) key for a new TOTP device is now forced to a unicode
+ string.
+
+
+v0.3.4 - January 10, 2016 - Python 3 cleanup
+--------------------------------------------
+
+- All modules include all four Python 3 __future__ imports for consistency.
+
+- Migrations no longer have byte strings in them.
+
+
+v0.3.3 - October 15, 2015 - Django 1.9
+--------------------------------------
+
+- Fix the addstatictoken management command under Django 1.9.
+
+
+v0.3.2 - October 11, 2015 - Django 1.8
+--------------------------------------
+
+- Stop importing models into the root of the package.
+
+- Use ModelAdmin.raw_id_fields for foreign keys to users.
+
+- General cleanup and compatibility with Django 1.9a1.
+
+
+v0.3.1 - April 3, 2015 - Django 1.8
+-----------------------------------
+
+- Add support for the new app registry, when available.
+
+- Add Django 1.8 to the test matrix and fix a few test bugs.
+
+
+v0.3.0 - February 7, 2015 - Support Django migrations
+-----------------------------------------------------
+
+- All plugins now have both Django and South migrations. Please see the `upgrade
+ notes`_ for details on upgrading from previous versions.
+
+.. _upgrade notes: https://pythonhosted.org/django-otp/overview.html#upgrading
+
+
+v0.2.7 - April 26, 2014 - Fix for Custom user models with South
+---------------------------------------------------------------
+
+- Updated the otp_totp South migrations to support custom user models. Thanks to
+ https://bitbucket.org/robirichter.
+
+
+v0.2.6 - April 18, 2014 - Fix for Python 3.2 with South
+-------------------------------------------------------
+
+- Removed South-generated unicode string literals.
+
+
+v0.2.4 - April 15, 2014 - TOTP plugin fix (migration warning)
+-------------------------------------------------------------
+
+- Per the RFC, :class:`~django_otp.plugins.otp_totp.models.TOTPDevice` will no
+ longer verify the same token twice.
+
+- Cosmetic fixes to the admin login form on Django 1.6.
+
+.. warning::
+
+ This includes a model change in TOTPDevice. If you are upgrading and your
+ project uses South, you should first convert it to South with ``manage
+ migrate otp_totp 0001 --fake``. If you're not using South, you will need to
+ generate and run the appropriate SQL manually.
+
+
+v0.2.3 - March 3, 2014 - Fix pickling
+-------------------------------------
+
+- OTPMiddleware no longer interferes with pickling request.user.
+
+
+v0.2.2 - December 31, 2013 - Require Django 1.4.2
+-------------------------------------------------
+
+- Update Django requirement to 1.4.2, the first version with django.utils.six.
+
+
+v0.2.1 - November 19, 2013 - Bug fix
+------------------------------------
+
+- Fix unicode representation of devices in some exotic scenarios.
+
+
+v0.2.0 - November 10, 2013 - Django 1.6
+---------------------------------------
+
+- Now supports Django 1.4 to 1.6 on Python 2.6, 2.7, 3.2, and 3.3. This is the
+ first release for Python 3.
+
+
+v0.1.8 - August 20, 2013 - user_has_device API
+-----------------------------------------------
+
+- Add :func:`django_otp.user_has_device` to detect whether a user has any
+ devices configured. This change supports a fix in django-otp-agents 0.1.4.
+
+
+v0.1.7 - July 3, 2013 - Decorator improvement
+-----------------------------------------------
+
+- Add if_configured argument to :func:`~django_otp.decorators.otp_required`.
+
+
+v0.1.6 - May 9, 2013 - Unit test improvements
+---------------------------------------------
+
+- Major unit test cleanup. Tests should pass or be skipped under all supported
+ versions of Django, with or without custom users and timzeone support.
+
+
+v0.1.5 - May 8, 2013 - OTPAdminSite improvement
+-----------------------------------------------
+
+- OTPAdminSite now selects an apporpriate login template automatically, based on
+ the current Django version. Django versions 1.3 to 1.5 are currently
+ supported.
+
+- Unit test cleanup.
+
+
+v0.1.3 - March 10, 2013 - Django 1.5 compatibility
+--------------------------------------------------
+
+- Add support for custom user models in Django 1.5.
+
+- Stop using ``Device.objects``: Django doesn't allow access to an abstract
+ model's manager any more.
+
+
+v0.1.2 - October 8, 2012 - Bug fix
+----------------------------------
+
+- Fix an exception when an empty login form is submitted.
+
+
+v0.1.0 - August 20, 2012 - Initial Release
+------------------------------------------
+
+Initial release.
+
+
+.. vim: ft=rst nospell tw=80
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..be76807
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2012, Peter Sagerson
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..3caa31a
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,7 @@
+include README.rst CHANGES LICENSE
+
+recursive-include docs *.rst *.py Makefile
+prune docs/build
+
+recursive-include django_otp/plugins/otp_email/templates *
+recursive-include django_otp/templates *
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..c4e393f
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,48 @@
+Metadata-Version: 1.1
+Name: django-otp
+Version: 0.3.8
+Summary: A pluggable framework for adding two-factor authentication to Django using one-time passwords.
+Home-page: https://bitbucket.org/psagers/django-otp
+Author: Peter Sagerson
+Author-email: psagersDjwublJf at ignorare.net
+License: BSD
+Description: This project makes it easy to add support for `one-time passwords
+ <http://en.wikipedia.org/wiki/One-time_password>`_ (OTPs) to Django. It can be
+ integrated at various levels, depending on how much customization is required.
+ It integrates with ``django.contrib.auth``, although it is not a Django
+ authentication backend. The primary target is developers wishing to incorporate
+ OTPs into their Django projects as a form of `two-factor authentication
+ <http://en.wikipedia.org/wiki/Two-factor_authentication>`_.
+
+ This project includes several simple OTP plugins and more are available
+ separately. This package also includes an implementation of OATH `HOTP
+ <http://tools.ietf.org/html/rfc4226>`_ and `TOTP
+ <http://tools.ietf.org/html/rfc6238>`_ for convenience, as these are standard
+ OTP algorithms used by multiple plugins.
+
+ This version is supported on Python 2.6, 2.7, and 3.3+; and Django >= 1.4.2.
+
+ .. warning::
+
+ All plugins now contain both South and Django migrations. If you're using
+ South or upgrading to Django 1.7, please see the `upgrade notes`_ in the
+ documentation first.
+
+ .. _upgrade notes: https://pythonhosted.org/django-otp/overview.html#upgrading
+
+ .. vim:ft=rst
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Framework :: Django
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Topic :: Security
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..bceb8bc
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,25 @@
+This project makes it easy to add support for `one-time passwords
+<http://en.wikipedia.org/wiki/One-time_password>`_ (OTPs) to Django. It can be
+integrated at various levels, depending on how much customization is required.
+It integrates with ``django.contrib.auth``, although it is not a Django
+authentication backend. The primary target is developers wishing to incorporate
+OTPs into their Django projects as a form of `two-factor authentication
+<http://en.wikipedia.org/wiki/Two-factor_authentication>`_.
+
+This project includes several simple OTP plugins and more are available
+separately. This package also includes an implementation of OATH `HOTP
+<http://tools.ietf.org/html/rfc4226>`_ and `TOTP
+<http://tools.ietf.org/html/rfc6238>`_ for convenience, as these are standard
+OTP algorithms used by multiple plugins.
+
+This version is supported on Python 2.6, 2.7, and 3.3+; and Django >= 1.4.2.
+
+.. warning::
+
+ All plugins now contain both South and Django migrations. If you're using
+ South or upgrading to Django 1.7, please see the `upgrade notes`_ in the
+ documentation first.
+
+.. _upgrade notes: https://pythonhosted.org/django-otp/overview.html#upgrading
+
+.. vim:ft=rst
diff --git a/django_otp.egg-info/PKG-INFO b/django_otp.egg-info/PKG-INFO
new file mode 100644
index 0000000..c4e393f
--- /dev/null
+++ b/django_otp.egg-info/PKG-INFO
@@ -0,0 +1,48 @@
+Metadata-Version: 1.1
+Name: django-otp
+Version: 0.3.8
+Summary: A pluggable framework for adding two-factor authentication to Django using one-time passwords.
+Home-page: https://bitbucket.org/psagers/django-otp
+Author: Peter Sagerson
+Author-email: psagersDjwublJf at ignorare.net
+License: BSD
+Description: This project makes it easy to add support for `one-time passwords
+ <http://en.wikipedia.org/wiki/One-time_password>`_ (OTPs) to Django. It can be
+ integrated at various levels, depending on how much customization is required.
+ It integrates with ``django.contrib.auth``, although it is not a Django
+ authentication backend. The primary target is developers wishing to incorporate
+ OTPs into their Django projects as a form of `two-factor authentication
+ <http://en.wikipedia.org/wiki/Two-factor_authentication>`_.
+
+ This project includes several simple OTP plugins and more are available
+ separately. This package also includes an implementation of OATH `HOTP
+ <http://tools.ietf.org/html/rfc4226>`_ and `TOTP
+ <http://tools.ietf.org/html/rfc6238>`_ for convenience, as these are standard
+ OTP algorithms used by multiple plugins.
+
+ This version is supported on Python 2.6, 2.7, and 3.3+; and Django >= 1.4.2.
+
+ .. warning::
+
+ All plugins now contain both South and Django migrations. If you're using
+ South or upgrading to Django 1.7, please see the `upgrade notes`_ in the
+ documentation first.
+
+ .. _upgrade notes: https://pythonhosted.org/django-otp/overview.html#upgrading
+
+ .. vim:ft=rst
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Framework :: Django
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Topic :: Security
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/django_otp.egg-info/SOURCES.txt b/django_otp.egg-info/SOURCES.txt
new file mode 100644
index 0000000..c20a3ba
--- /dev/null
+++ b/django_otp.egg-info/SOURCES.txt
@@ -0,0 +1,78 @@
+CHANGES
+LICENSE
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+django_otp/__init__.py
+django_otp/admin.py
+django_otp/conf.py
+django_otp/decorators.py
+django_otp/forms.py
+django_otp/middleware.py
+django_otp/models.py
+django_otp/oath.py
+django_otp/tests.py
+django_otp/util.py
+django_otp/views.py
+django_otp.egg-info/PKG-INFO
+django_otp.egg-info/SOURCES.txt
+django_otp.egg-info/dependency_links.txt
+django_otp.egg-info/not-zip-safe
+django_otp.egg-info/requires.txt
+django_otp.egg-info/top_level.txt
+django_otp/plugins/__init__.py
+django_otp/plugins/otp_email/__init__.py
+django_otp/plugins/otp_email/admin.py
+django_otp/plugins/otp_email/conf.py
+django_otp/plugins/otp_email/models.py
+django_otp/plugins/otp_email/tests.py
+django_otp/plugins/otp_email/migrations/0001_initial.py
+django_otp/plugins/otp_email/migrations/__init__.py
+django_otp/plugins/otp_email/south_migrations/0001_initial.py
+django_otp/plugins/otp_email/south_migrations/__init__.py
+django_otp/plugins/otp_email/templates/otp/email/token.txt
+django_otp/plugins/otp_hotp/__init__.py
+django_otp/plugins/otp_hotp/admin.py
+django_otp/plugins/otp_hotp/models.py
+django_otp/plugins/otp_hotp/tests.py
+django_otp/plugins/otp_hotp/migrations/0001_initial.py
+django_otp/plugins/otp_hotp/migrations/__init__.py
+django_otp/plugins/otp_hotp/south_migrations/0001_initial.py
+django_otp/plugins/otp_hotp/south_migrations/__init__.py
+django_otp/plugins/otp_static/__init__.py
+django_otp/plugins/otp_static/admin.py
+django_otp/plugins/otp_static/lib.py
+django_otp/plugins/otp_static/models.py
+django_otp/plugins/otp_static/tests.py
+django_otp/plugins/otp_static/management/__init__.py
+django_otp/plugins/otp_static/management/commands/__init__.py
+django_otp/plugins/otp_static/management/commands/addstatictoken.py
+django_otp/plugins/otp_static/migrations/0001_initial.py
+django_otp/plugins/otp_static/migrations/__init__.py
+django_otp/plugins/otp_static/south_migrations/0001_initial.py
+django_otp/plugins/otp_static/south_migrations/__init__.py
+django_otp/plugins/otp_totp/__init__.py
+django_otp/plugins/otp_totp/admin.py
+django_otp/plugins/otp_totp/models.py
+django_otp/plugins/otp_totp/tests.py
+django_otp/plugins/otp_totp/migrations/0001_initial.py
+django_otp/plugins/otp_totp/migrations/__init__.py
+django_otp/plugins/otp_totp/south_migrations/0001_initial.py
+django_otp/plugins/otp_totp/south_migrations/0002_last_t.py
+django_otp/plugins/otp_totp/south_migrations/__init__.py
+django_otp/templates/otp/.DS_Store
+django_otp/templates/otp/admin14/login.html
+django_otp/templates/otp/admin15/login.html
+django_otp/templates/otp/admin16/login.html
+django_otp/templates/otp/admin17/login.html
+django_otp/templates/otp/admin18/login.html
+django_otp/templates/otp/admin19/login.html
+docs/Makefile
+docs/ext/otpdocs.py
+docs/source/auth.rst
+docs/source/changes.rst
+docs/source/conf.py
+docs/source/extend.rst
+docs/source/index.rst
+docs/source/overview.rst
\ No newline at end of file
diff --git a/django_otp.egg-info/dependency_links.txt b/django_otp.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/django_otp.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/django_otp.egg-info/not-zip-safe b/django_otp.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/django_otp.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/django_otp.egg-info/requires.txt b/django_otp.egg-info/requires.txt
new file mode 100644
index 0000000..5137bb2
--- /dev/null
+++ b/django_otp.egg-info/requires.txt
@@ -0,0 +1 @@
+django >= 1.4.2
diff --git a/django_otp.egg-info/top_level.txt b/django_otp.egg-info/top_level.txt
new file mode 100644
index 0000000..54bbdb1
--- /dev/null
+++ b/django_otp.egg-info/top_level.txt
@@ -0,0 +1 @@
+django_otp
diff --git a/django_otp/__init__.py b/django_otp/__init__.py
new file mode 100644
index 0000000..7ba03d8
--- /dev/null
+++ b/django_otp/__init__.py
@@ -0,0 +1,164 @@
+import sys
+
+import django
+from django.contrib.auth.signals import user_logged_in
+
+
+DEVICE_ID_SESSION_KEY = 'otp_device_id'
+
+
+def login(request, device):
+ """
+ Persist the given OTP device in the current session. The device will be
+ rejected if it does not belong to ``request.user``.
+
+ This is called automatically any time :func:`django.contrib.auth.login` is
+ called with a user having an ``otp_device`` atribute. If you use Django's
+ :func:`~django.contrib.auth.views.login` view with the django-otp
+ authentication forms, then you won't need to call this.
+
+ :param request: The HTTP request
+ :type request: :class:`~django.http.HttpRequest`
+
+ :param device: The OTP device used to verify the user.
+ :type device: :class:`~django_otp.models.Device`
+ """
+ user = getattr(request, 'user', None)
+
+ if (user is not None) and (device is not None) and (device.user_id == user.id):
+ request.session[DEVICE_ID_SESSION_KEY] = device.persistent_id
+ request.user.otp_device = device
+
+
+def _handle_auth_login(sender, request, user, **kwargs):
+ """
+ Automatically persists an OTP device that was set by an OTP-aware
+ AuthenticationForm.
+ """
+ if hasattr(user, 'otp_device'):
+ login(request, user.otp_device)
+
+user_logged_in.connect(_handle_auth_login)
+
+
+def match_token(user, token):
+ """
+ Attempts to verify a :term:`token` on every device attached to the given
+ user until one of them succeeds. When possible, you should prefer to verify
+ tokens against specific devices.
+
+ :param user: The user supplying the token.
+ :type user: :class:`~django.contrib.auth.models.User`
+
+ :param string token: An OTP token to verify.
+
+ :returns: The device that accepted ``token``, if any.
+ :rtype: :class:`~django_otp.models.Device` or ``None``
+ """
+ matches = (d for d in devices_for_user(user) if d.verify_token(token))
+
+ return next(matches, None)
+
+
+def devices_for_user(user, confirmed=True):
+ """
+ Return an iterable of all devices registered to the given user.
+
+ Returns an empty iterable for anonymous users.
+
+ :param user: standard or custom user object.
+ :type user: :class:`~django.contrib.auth.models.User`
+
+ :param confirmed: If ``None``, all matching devices are returned.
+ Otherwise, this can be any true or false value to limit the query
+ to confirmed or unconfirmed devices, respectively.
+
+ :rtype: iterable
+ """
+ if _user_is_anonymous(user):
+ return
+
+ for model in device_classes():
+ for device in model.objects.devices_for_user(user, confirmed=confirmed):
+ yield device
+
+
+def user_has_device(user, confirmed=True):
+ """
+ Return ``True`` if the user has at least one device.
+
+ Returns ``False`` for anonymous users.
+
+ :param user: standard or custom user object.
+ :type user: :class:`~django.contrib.auth.models.User`
+
+ :param confirmed: If ``None``, all matching devices are considered.
+ Otherwise, this can be any true or false value to limit the query
+ to confirmed or unconfirmed devices, respectively.
+ """
+ try:
+ next(devices_for_user(user, confirmed=confirmed))
+ except StopIteration:
+ has_device = False
+ else:
+ has_device = True
+
+ return has_device
+
+
+def device_classes():
+ """
+ Returns an iterable of all loaded device models.
+ """
+ from django_otp.models import Device
+
+ try:
+ from django.apps import apps
+ except ImportError:
+ for model in _device_classes_legacy():
+ yield model
+ else:
+ for config in apps.get_app_configs():
+ for model in config.get_models():
+ if issubclass(model, Device):
+ yield model
+
+
+def _device_classes_legacy():
+ """
+ Find device models in Django < 1.7.
+ """
+ from django.db.models import get_apps, get_models
+ from django_otp.models import Device
+
+ for app in get_apps():
+ for model in get_models(app):
+ if issubclass(model, Device):
+ yield model
+
+
+def import_class(path):
+ """
+ Imports a class based on a full Python path ('pkg.pkg.mod.Class'). This
+ does not trap any exceptions if the path is not valid.
+ """
+ module, name = path.rsplit('.', 1)
+ __import__(module)
+ mod = sys.modules[module]
+ cls = getattr(mod, name)
+
+ return cls
+
+
+def _user_is_authenticated(user):
+ """
+ Wraps django's user.is_authenticated to support both Django 2 and pre-1.10.
+ """
+ return user.is_authenticated if (django.VERSION >= (1, 10)) else user.is_authenticated()
+
+
+def _user_is_anonymous(user):
+ """
+ Wraps django's user.is_anonymous to support both Django 2 and pre-1.10.
+ """
+ return user.is_anonymous if (django.VERSION >= (1, 10)) else user.is_anonymous()
diff --git a/django_otp/admin.py b/django_otp/admin.py
new file mode 100644
index 0000000..a716aca
--- /dev/null
+++ b/django_otp/admin.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import django
+from django import forms
+from django.contrib.admin.forms import AdminAuthenticationForm
+from django.contrib.admin.sites import AdminSite
+
+from .forms import OTPAuthenticationFormMixin
+
+
+def _admin_template_for_django_version():
+ minor_django_version = django.VERSION[:2]
+
+ if minor_django_version <= (1, 4):
+ return 'otp/admin14/login.html'
+ elif minor_django_version == (1, 5):
+ return 'otp/admin15/login.html'
+ elif minor_django_version == (1, 6):
+ return 'otp/admin16/login.html'
+ elif minor_django_version == (1, 7):
+ return 'otp/admin17/login.html'
+ elif minor_django_version == (1, 8):
+ return 'otp/admin18/login.html'
+ else:
+ return 'otp/admin19/login.html'
+
+
+class OTPAdminAuthenticationForm(AdminAuthenticationForm, OTPAuthenticationFormMixin):
+ """
+ An :class:`~django.contrib.admin.forms.AdminAuthenticationForm` subclass
+ that solicits an OTP token. This has the same behavior as
+ :class:`~django_otp.forms.OTPAuthenticationForm`.
+ """
+ otp_device = forms.CharField(required=False, widget=forms.Select)
+ otp_token = forms.CharField(required=False)
+
+ # This is a placeholder field that allows us to detect when the user clicks
+ # the otp_challenge submit button.
+ otp_challenge = forms.CharField(required=False)
+
+ def __init__(self, *args, **kwargs):
+ super(OTPAdminAuthenticationForm, self).__init__(*args, **kwargs)
+
+ # A litle extra cheese to make it prettier.
+ minor_django_version = django.VERSION[:2]
+
+ if minor_django_version < (1, 6):
+ self.fields['otp_token'].widget.attrs['style'] = 'width: 14em;'
+
+ def clean(self):
+ self.cleaned_data = super(OTPAdminAuthenticationForm, self).clean()
+ self.clean_otp(self.get_user())
+
+ return self.cleaned_data
+
+
+class OTPAdminSite(AdminSite):
+ """
+ This is an :class:`~django.contrib.admin.AdminSite` subclass that requires
+ two-factor authentication. Only users that can be verified by a registered
+ OTP device will be authorized for this admin site. Unverified users will be
+ treated as if :attr:`~django.contrib.auth.models.User.is_staff` is
+ ``False``.
+ """
+ #: The default instance name of this admin site. You should instantiate
+ #: this class as ``OTPAdminSite(OTPAdminSite.name)`` to make sure the admin
+ #: templates render the correct URLs.
+ name = 'otpadmin'
+
+ login_form = OTPAdminAuthenticationForm
+
+ #: We automatically select a modified login template based on your Django
+ #: version. If it doesn't look right, your version may not be supported, in
+ #: which case feel free to replace it.
+ login_template = _admin_template_for_django_version()
+
+ def __init__(self, name='otpadmin'):
+ super(OTPAdminSite, self).__init__(name)
+
+ def has_permission(self, request):
+ """
+ In addition to the default requirements, this only allows access to
+ users who have been verified by a registered OTP device.
+ """
+ return super(OTPAdminSite, self).has_permission(request) and request.user.is_verified()
diff --git a/django_otp/conf.py b/django_otp/conf.py
new file mode 100644
index 0000000..129385b
--- /dev/null
+++ b/django_otp/conf.py
@@ -0,0 +1,27 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import django.conf
+from django.utils.six import iteritems
+
+
+class Settings(object):
+ """
+ This is a simple class to take the place of the global settings object. An
+ instance will contain all of our settings as attributes, with default values
+ if they are not specified by the configuration.
+ """
+ defaults = {
+ 'OTP_LOGIN_URL': django.conf.settings.LOGIN_URL,
+ }
+
+ def __init__(self):
+ """
+ Loads our settings from django.conf.settings, applying defaults for any
+ that are omitted.
+ """
+ for name, default in iteritems(self.defaults):
+ value = getattr(django.conf.settings, name, default)
+ setattr(self, name, value)
+
+
+settings = Settings()
diff --git a/django_otp/decorators.py b/django_otp/decorators.py
new file mode 100644
index 0000000..d4e1e32
--- /dev/null
+++ b/django_otp/decorators.py
@@ -0,0 +1,27 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from django.contrib.auth.decorators import user_passes_test
+
+from django_otp import user_has_device, _user_is_authenticated
+from django_otp.conf import settings
+
+
+def otp_required(view=None, redirect_field_name='next', login_url=None, if_configured=False):
+ """
+ Similar to :func:`~django.contrib.auth.decorators.login_required`, but
+ requires the user to be :term:`verified`. By default, this redirects users
+ to :setting:`OTP_LOGIN_URL`.
+
+ :param if_configured: If ``True``, an authenticated user with no confirmed
+ OTP devices will be allowed. Default is ``False``.
+ :type if_configured: bool
+ """
+ if login_url is None:
+ login_url = settings.OTP_LOGIN_URL
+
+ def test(user):
+ return user.is_verified() or (if_configured and _user_is_authenticated(user) and not user_has_device(user))
+
+ decorator = user_passes_test(test, login_url=login_url, redirect_field_name=redirect_field_name)
+
+ return decorator if (view is None) else decorator(view)
diff --git a/django_otp/forms.py b/django_otp/forms.py
new file mode 100644
index 0000000..dc71f50
--- /dev/null
+++ b/django_otp/forms.py
@@ -0,0 +1,250 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.auth.forms import AuthenticationForm
+
+from . import match_token, devices_for_user
+from .models import Device
+
+
+class OTPAuthenticationFormMixin(object):
+ """
+ Shared functionality for
+ :class:`~django.contrib.auth.forms.AuthenticationForm` subclasses that wish
+ to handle OTP tokens. Subclasses must do the following in order to use
+ this:
+
+ #. Define three additional form fields::
+
+ otp_device = forms.CharField(required=False, widget=forms.Select)
+ otp_token = forms.CharField(required=False)
+ otp_challenge = forms.CharField(required=False)
+
+ - ``otp_device`` will be a select widget with all of the user's
+ devices listed. Until the user has entered a valid username and
+ password, this will be empty and may be omitted.
+ - ``otp_token`` is where the user will enter their token.
+ - ``otp_challenge`` is a placeholder field that captures an
+ alternate submit button of the same name.
+
+ #. Override :meth:`~django.forms.Form.clean` to call :meth:`clean_otp`
+ after invoking the inherited :meth:`~django.forms.Form.clean`. See
+ :class:`OTPAuthenticationForm` for an example.
+
+ #. See :class:`OTPAuthenticationForm` for information about writing a
+ login template for this form. The file
+ ``django_otp/templates/otp/admin/login.html`` is also a useful
+ example.
+
+ You will most likely be able to use :class:`OTPAuthenticationForm`,
+ :class:`django_otp.admin.OTPAdminAuthenticationForm`, or
+ :class:`OTPTokenForm` directly. If these do not suit your needs--for
+ instance if your primary authentication is not by password--they should
+ serve as useful examples.
+ """
+ def clean_otp(self, user):
+ """
+ Processes the ``otp_*`` fields.
+
+ :param user: A user that has been authenticated by the first factor
+ (such as a password).
+ :type user: :class:`~django.contrib.auth.models.User`
+ :rasies: :exc:`~django.core.exceptions.ValidationError` if the user is
+ not fully authenticated by an OTP token.
+ """
+ if user is None:
+ return
+
+ device = self._chosen_device(user)
+ token = self.cleaned_data.get('otp_token')
+
+ user.otp_device = None
+
+ try:
+ if self.cleaned_data.get('otp_challenge'):
+ self._handle_challenge(device)
+ elif token:
+ user.otp_device = self._verify_token(user, token, device)
+ else:
+ raise forms.ValidationError(_('Please enter your OTP token.'), code='required')
+ finally:
+ if user.otp_device is None:
+ self._update_form(user)
+
+ def _chosen_device(self, user):
+ device_id = self.cleaned_data.get('otp_device')
+
+ if device_id:
+ device = Device.from_persistent_id(device_id)
+ else:
+ device = None
+
+ # SECURITY: The form doesn't validate otp_device for us, since we don't
+ # have the list of choices until we authenticate the user. Without the
+ # following, an attacker could authenticate using some other user's OTP
+ # device.
+ if (device is not None) and (device.user_id != user.id):
+ device = None
+
+ return device
+
+ def _handle_challenge(self, device):
+ try:
+ challenge = device.generate_challenge() if (device is not None) else None
+ except Exception as e:
... 4136 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-otp.git
More information about the Python-modules-commits
mailing list