[Python-modules-commits] [python-django-otp] 01/01: New upstream version 0.4.1.1

Michael Fladischer fladi at moszumanska.debian.org
Wed Nov 1 19:42:49 UTC 2017


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch upstream
in repository python-django-otp.

commit bffb898f169b6a6472907713482d7d99361aaf6b
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Wed Nov 1 20:23:03 2017 +0100

    New upstream version 0.4.1.1
---
 CHANGES                                | 10 ++++++++++
 PKG-INFO                               | 10 ++++++----
 README.rst                             |  6 ++++--
 django_otp.egg-info/PKG-INFO           | 10 ++++++----
 django_otp/__init__.py                 | 16 +--------------
 django_otp/middleware.py               | 17 ++++++++++++++--
 django_otp/models.py                   | 34 ++++++++++++++++++++------------
 django_otp/plugins/otp_email/models.py |  3 ++-
 django_otp/plugins/otp_email/tests.py  |  2 +-
 django_otp/plugins/otp_hotp/models.py  |  3 ++-
 django_otp/plugins/otp_static/tests.py |  6 +++---
 django_otp/tests.py                    | 36 ++++++++++++++++++++++++++++++++++
 docs/source/conf.py                    |  2 +-
 docs/source/overview.rst               |  2 +-
 setup.py                               |  2 +-
 15 files changed, 111 insertions(+), 48 deletions(-)

diff --git a/CHANGES b/CHANGES
index a0a1b43..bc491e9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,13 @@
+v0.4.1 - August 29, 2017 - Misc fixes
+-------------------------------------
+
+- Improved handling of device persistent identifiers.
+
+- Fix `#25`_: make sure default keys are unicode values.
+
+.. _#25: https://bitbucket.org/psagers/django-otp/issues/25/attributeerror-bytes-object-has-no
+
+
 v0.4.0 - July 19, 2017 - Update support matrix
 ----------------------------------------------
 
diff --git a/PKG-INFO b/PKG-INFO
index 006144f..1e55e7d 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,14 +1,12 @@
 Metadata-Version: 1.1
 Name: django-otp
-Version: 0.4.0.1
+Version: 0.4.1.1
 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: psagers at ignorare.net
 License: BSD
-Description: `Package Documentation <http://django-otp-official.readthedocs.io/>`_
-        
-        This project makes it easy to add support for `one-time passwords
+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
@@ -22,6 +20,10 @@ Description: `Package Documentation <http://django-otp-official.readthedocs.io/>
         <http://tools.ietf.org/html/rfc6238>`_ for convenience, as these are standard
         OTP algorithms used by multiple plugins.
         
+        * Repository: https://bitbucket.org/psagers/django-otp
+        * Documentation: https://django-otp-official.readthedocs.io/
+        * Mailing list: https://groups.google.com/forum/#!forum/django-otp
+        
         This version is supported on Python 2.7 and 3.4+; and Django 1.8 and 1.10+.
         
         .. _upgrade notes: https://pythonhosted.org/django-otp/overview.html#upgrading
diff --git a/README.rst b/README.rst
index 1883575..9c41ca3 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,3 @@
-`Package Documentation <http://django-otp-official.readthedocs.io/>`_
-
 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.
@@ -14,6 +12,10 @@ separately. This package also includes an implementation of OATH `HOTP
 <http://tools.ietf.org/html/rfc6238>`_ for convenience, as these are standard
 OTP algorithms used by multiple plugins.
 
+* Repository: https://bitbucket.org/psagers/django-otp
+* Documentation: https://django-otp-official.readthedocs.io/
+* Mailing list: https://groups.google.com/forum/#!forum/django-otp
+
 This version is supported on Python 2.7 and 3.4+; and Django 1.8 and 1.10+.
 
 .. _upgrade notes: https://pythonhosted.org/django-otp/overview.html#upgrading
diff --git a/django_otp.egg-info/PKG-INFO b/django_otp.egg-info/PKG-INFO
index 006144f..1e55e7d 100644
--- a/django_otp.egg-info/PKG-INFO
+++ b/django_otp.egg-info/PKG-INFO
@@ -1,14 +1,12 @@
 Metadata-Version: 1.1
 Name: django-otp
-Version: 0.4.0.1
+Version: 0.4.1.1
 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: psagers at ignorare.net
 License: BSD
-Description: `Package Documentation <http://django-otp-official.readthedocs.io/>`_
-        
-        This project makes it easy to add support for `one-time passwords
+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
@@ -22,6 +20,10 @@ Description: `Package Documentation <http://django-otp-official.readthedocs.io/>
         <http://tools.ietf.org/html/rfc6238>`_ for convenience, as these are standard
         OTP algorithms used by multiple plugins.
         
+        * Repository: https://bitbucket.org/psagers/django-otp
+        * Documentation: https://django-otp-official.readthedocs.io/
+        * Mailing list: https://groups.google.com/forum/#!forum/django-otp
+        
         This version is supported on Python 2.7 and 3.4+; and Django 1.8 and 1.10+.
         
         .. _upgrade notes: https://pythonhosted.org/django-otp/overview.html#upgrading
diff --git a/django_otp/__init__.py b/django_otp/__init__.py
index 7ba03d8..1808218 100644
--- a/django_otp/__init__.py
+++ b/django_otp/__init__.py
@@ -1,5 +1,3 @@
-import sys
-
 import django
 from django.contrib.auth.signals import user_logged_in
 
@@ -38,6 +36,7 @@ def _handle_auth_login(sender, request, user, **kwargs):
     if hasattr(user, 'otp_device'):
         login(request, user.otp_device)
 
+
 user_logged_in.connect(_handle_auth_login)
 
 
@@ -137,19 +136,6 @@ def _device_classes_legacy():
                 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.
diff --git a/django_otp/middleware.py b/django_otp/middleware.py
index 30609fd..294571a 100644
--- a/django_otp/middleware.py
+++ b/django_otp/middleware.py
@@ -43,8 +43,8 @@ class OTPMiddleware(MiddlewareMixin):
         user.is_verified = functools.partial(is_verified, user)
 
         if _user_is_authenticated(user):
-            device_id = request.session.get(DEVICE_ID_SESSION_KEY)
-            device = Device.from_persistent_id(device_id) if device_id else None
+            persistent_id = request.session.get(DEVICE_ID_SESSION_KEY)
+            device = self._device_from_persistent_id(persistent_id) if persistent_id else None
 
             if (device is not None) and (device.user_id != user.id):
                 device = None
@@ -55,3 +55,16 @@ class OTPMiddleware(MiddlewareMixin):
             user.otp_device = device
 
         return user
+
+    def _device_from_persistent_id(self, persistent_id):
+        # Convert legacy persistent_id values (these used to be full import
+        # paths). This won't work for apps with models in sub-modules, but that
+        # should be pretty rare. And the worst that happens is the user has to
+        # log in again.
+        if persistent_id.count('.') > 1:
+            parts = persistent_id.split('.')
+            persistent_id = '.'.join((parts[-3], parts[-1]))
+
+        device = Device.from_persistent_id(persistent_id)
+
+        return device
diff --git a/django_otp/models.py b/django_otp/models.py
index 48fbddb..6751e3b 100644
--- a/django_otp/models.py
+++ b/django_otp/models.py
@@ -1,5 +1,6 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
+from django.apps import apps
 from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
@@ -92,28 +93,37 @@ class Device(models.Model):
 
     @property
     def persistent_id(self):
-        return '{0}/{1}'.format(self.import_path, self.id)
+        return '{0}/{1}'.format(self.model_label(), self.id)
 
-    @property
-    def import_path(self):
-        return '{0}.{1}'.format(self.__module__, self.__class__.__name__)
+    @classmethod
+    def model_label(cls):
+        """
+        Returns an identifier for this Django model class.
+
+        This is just the standard "<app_label>.<model_name>" form.
+
+        """
+        return '{0}.{1}'.format(cls._meta.app_label, cls._meta.model_name)
 
     @classmethod
-    def from_persistent_id(cls, path):
+    def from_persistent_id(cls, persistent_id):
         """
         Loads a device from its persistent id::
 
             device == Device.from_persistent_id(device.persistent_id)
+
         """
-        from . import import_class
+        device = None
 
         try:
-            device_type, device_id = path.rsplit('/', 1)
-
-            device_cls = import_class(device_type)
-            device = device_cls.objects.get(id=device_id)
-        except Exception:
-            device = None
+            model_label, device_id = persistent_id.rsplit('/', 1)
+            app_label, model_name = model_label.split('.')
+
+            device_cls = apps.get_model(app_label, model_name)
+            if issubclass(device_cls, Device):
+                device = device_cls.objects.filter(id=int(device_id)).first()
+        except (ValueError, LookupError):
+            pass
 
         return device
 
diff --git a/django_otp/plugins/otp_email/models.py b/django_otp/plugins/otp_email/models.py
index 4ef2a6f..afa0aa6 100644
--- a/django_otp/plugins/otp_email/models.py
+++ b/django_otp/plugins/otp_email/models.py
@@ -5,6 +5,7 @@ from binascii import unhexlify
 from django.core.mail import send_mail
 from django.db import models
 from django.template.loader import render_to_string
+from django.utils.encoding import force_text
 
 from django_otp.models import Device
 from django_otp.oath import totp
@@ -14,7 +15,7 @@ from .conf import settings
 
 
 def default_key():
-    return random_hex(20)
+    return force_text(random_hex(20))
 
 
 def key_validator(value):
diff --git a/django_otp/plugins/otp_email/tests.py b/django_otp/plugins/otp_email/tests.py
index eecb73e..7cb6b45 100644
--- a/django_otp/plugins/otp_email/tests.py
+++ b/django_otp/plugins/otp_email/tests.py
@@ -29,7 +29,7 @@ class AuthFormTest(TestCase):
         data = {
             'username': 'alice',
             'password': 'password',
-            'otp_device': 'django_otp.plugins.otp_email.models.EmailDevice/1',
+            'otp_device': 'otp_email.emaildevice/1',
             'otp_token': '',
             'otp_challenge': '1',
         }
diff --git a/django_otp/plugins/otp_hotp/models.py b/django_otp/plugins/otp_hotp/models.py
index ff8b49e..3aa375a 100644
--- a/django_otp/plugins/otp_hotp/models.py
+++ b/django_otp/plugins/otp_hotp/models.py
@@ -5,6 +5,7 @@ from binascii import unhexlify
 
 from django.conf import settings
 from django.db import models
+from django.utils.encoding import force_text
 from django.utils.six import string_types
 from django.utils.six.moves.urllib.parse import quote, urlencode
 
@@ -14,7 +15,7 @@ from django_otp.util import random_hex, hex_validator
 
 
 def default_key():
-    return random_hex(20)
+    return force_text(random_hex(20))
 
 
 def key_validator(value):
diff --git a/django_otp/plugins/otp_static/tests.py b/django_otp/plugins/otp_static/tests.py
index 9f20edb..4ff5cf8 100644
--- a/django_otp/plugins/otp_static/tests.py
+++ b/django_otp/plugins/otp_static/tests.py
@@ -127,7 +127,7 @@ class AuthFormTest(TestCase):
         data = {
             'username': 'alice',
             'password': 'password',
-            'otp_device': 'django_otp.plugins.otp_static.models.StaticDevice/2',
+            'otp_device': 'otp_static.staticdevice/2',
             'otp_token': 'bob1',
         }
         form = OTPAuthenticationForm(None, data)
@@ -141,7 +141,7 @@ class AuthFormTest(TestCase):
         data = {
             'username': 'alice',
             'password': 'password',
-            'otp_device': 'django_otp.plugins.otp_email.models.StaticDevice/1',
+            'otp_device': 'otp_email.staticdevice/1',
             'otp_token': 'bogus',
         }
         form = OTPAuthenticationForm(None, data)
@@ -155,7 +155,7 @@ class AuthFormTest(TestCase):
         data = {
             'username': 'alice',
             'password': 'password',
-            'otp_device': 'django_otp.plugins.otp_static.models.StaticDevice/1',
+            'otp_device': 'otp_static.staticdevice/1',
             'otp_token': 'alice1',
         }
         form = OTPAuthenticationForm(None, data)
diff --git a/django_otp/tests.py b/django_otp/tests.py
index 0cdd515..61eaad4 100644
--- a/django_otp/tests.py
+++ b/django_otp/tests.py
@@ -84,6 +84,20 @@ class OTPMiddlewareTestCase(TestCase):
 
         self.assertTrue(request.user.is_verified())
 
+    def test_verified_legacy_device_id(self):
+        request = self.factory.get('/')
+        request.user = self.alice
+        device = self.alice.staticdevice_set.get()
+        request.session = {
+            DEVICE_ID_SESSION_KEY: '{}.{}/{}'.format(
+                device.__module__, device.__class__.__name__, device.id
+            )
+        }
+
+        self.middleware.process_request(request)
+
+        self.assertTrue(request.user.is_verified())
+
     def test_unverified(self):
         request = self.factory.get('/')
         request.user = self.alice
@@ -93,6 +107,28 @@ class OTPMiddlewareTestCase(TestCase):
 
         self.assertFalse(request.user.is_verified())
 
+    def test_no_device(self):
+        request = self.factory.get('/')
+        request.user = self.alice
+        request.session = {
+            DEVICE_ID_SESSION_KEY: 'otp_static.staticdevice/0',
+        }
+
+        self.middleware.process_request(request)
+
+        self.assertFalse(request.user.is_verified())
+
+    def test_no_model(self):
+        request = self.factory.get('/')
+        request.user = self.alice
+        request.session = {
+            DEVICE_ID_SESSION_KEY: 'otp_bogus.bogusdevice/0',
+        }
+
+        self.middleware.process_request(request)
+
+        self.assertFalse(request.user.is_verified())
+
     def test_wrong_user(self):
         request = self.factory.get('/')
         request.user = self.alice
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 73d0c10..5107de5 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -89,7 +89,7 @@ copyright = u'2012, Peter Sagerson'
 # The short X.Y version.
 version = '0.4'
 # The full version, including alpha/beta/rc tags.
-release = '0.4.0'
+release = '0.4.1'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/docs/source/overview.rst b/docs/source/overview.rst
index 96b26a0..de3b028 100644
--- a/docs/source/overview.rst
+++ b/docs/source/overview.rst
@@ -80,7 +80,7 @@ The recommended procedure is:
     2. Upgrade Django to 1.7 or later.
     3. Upgrade django-otp to the latest version.
 
-django-otp 0.4 will drop support for Django < 1.7.
+django-otp 0.4 dropped support for Django < 1.7.
 
 
 Upgrading from 0.2.3 or Earlier
diff --git a/setup.py b/setup.py
index b1e41b2..5fccd0e 100755
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
 
 setup(
     name='django-otp',
-    version='0.4.0.1',
+    version='0.4.1.1',
     description='A pluggable framework for adding two-factor authentication to Django using one-time passwords.',
     long_description=open('README.rst').read(),
     author='Peter Sagerson',

-- 
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