Skip to content

Commit

Permalink
Drop support for Python 2
Browse files Browse the repository at this point in the history
  • Loading branch information
claudep authored and psagers committed Jan 2, 2020
1 parent a060bdd commit 22447c1
Show file tree
Hide file tree
Showing 39 changed files with 54 additions and 180 deletions.
3 changes: 1 addition & 2 deletions .isort.cfg
Expand Up @@ -4,8 +4,7 @@ line_length = 120
lines_after_imports = 2
multi_line_output = 5

sections = FUTURE,STDLIB,SIX,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER
known_six = six
sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER
known_third_party = freezegun
known_django = django
known_first_party = django_otp
Expand Down
7 changes: 0 additions & 7 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions docs/source/conf.py
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# django-otp documentation build configuration file, created by
# sphinx-quickstart on Fri Jul 13 09:48:33 2012.
#
Expand Down Expand Up @@ -81,8 +79,8 @@
master_doc = 'index'

# General information about the project.
project = u'django-otp'
copyright = u'2012, Peter Sagerson'
project = 'django-otp'
copyright = '2012, Peter Sagerson'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -225,7 +223,7 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'django-otp.tex', u'django-otp Documentation',
('index', 'django-otp.tex', 'django-otp Documentation',
'Peter Sagerson', 'manual'),
]

Expand Down Expand Up @@ -255,7 +253,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'django-otp', u'django-otp Documentation',
('index', 'django-otp', 'django-otp Documentation',
['Peter Sagerson'], 1)
]

Expand All @@ -269,7 +267,7 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'django-otp', u'django-otp Documentation',
('index', 'django-otp', 'django-otp Documentation',
'Peter Sagerson', 'django-otp', 'One line description of project.',
'Miscellaneous'),
]
Expand Down
2 changes: 1 addition & 1 deletion docs/source/extend.rst
Expand Up @@ -31,7 +31,7 @@ interesting. Here's a simple implementation of a generic TOTP device::
key = models.CharField(max_length=80,
validators=[hex_validator()],
default=lambda: random_hex(20),
help_text=u'A hex-encoded secret key of up to 40 bytes.')
help_text='A hex-encoded secret key of up to 40 bytes.')

@property
def bin_key(self):
Expand Down
3 changes: 0 additions & 3 deletions setup.cfg
Expand Up @@ -7,6 +7,3 @@ ignore =
W504
# line too long
E501

[bdist_wheel]
universal = 1
3 changes: 1 addition & 2 deletions setup.py
Expand Up @@ -54,8 +54,8 @@ def find_package_data(*args, **kwargs):
"Framework :: Django",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Security",
"Topic :: Software Development :: Libraries :: Python Modules",
],
Expand All @@ -72,7 +72,6 @@ def find_package_data(*args, **kwargs):

install_requires=[
'django >= 1.11',
'six >= 1.10.0'
],
extras_require={
'qrcode': ['qrcode'],
Expand Down
17 changes: 3 additions & 14 deletions src/django_otp/admin.py
@@ -1,5 +1,3 @@
from __future__ import absolute_import, division, print_function, unicode_literals

import django
from django import forms
from django.contrib.admin.forms import AdminAuthenticationForm
Expand Down Expand Up @@ -35,17 +33,8 @@ class OTPAdminAuthenticationForm(AdminAuthenticationForm, OTPAuthenticationFormM
# 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.cleaned_data = super().clean()
self.clean_otp(self.get_user())

return self.cleaned_data
Expand All @@ -72,11 +61,11 @@ class OTPAdminSite(AdminSite):
login_template = _admin_template_for_django_version()

def __init__(self, name='otpadmin'):
super(OTPAdminSite, self).__init__(name)
super().__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()
return super().has_permission(request) and request.user.is_verified()
8 changes: 2 additions & 6 deletions src/django_otp/conf.py
@@ -1,11 +1,7 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from six import iteritems

import django.conf


class Settings(object):
class Settings:
"""
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
Expand All @@ -20,7 +16,7 @@ def __init__(self):
Loads our settings from django.conf.settings, applying defaults for any
that are omitted.
"""
for name, default in iteritems(self.defaults):
for name, default in self.defaults.items():
value = getattr(django.conf.settings, name, default)
setattr(self, name, value)

Expand Down
2 changes: 0 additions & 2 deletions src/django_otp/decorators.py
@@ -1,5 +1,3 @@
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
Expand Down
16 changes: 7 additions & 9 deletions src/django_otp/forms.py
@@ -1,15 +1,13 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy

from . import devices_for_user, match_token
from .models import Device, VerifyNotAllowed


class OTPAuthenticationFormMixin(object):
class OTPAuthenticationFormMixin:
"""
Shared functionality for
:class:`~django.contrib.auth.forms.AuthenticationForm` subclasses that wish
Expand Down Expand Up @@ -62,7 +60,7 @@ class CustomAuthForm(OTPAuthenticationFormMixin, AuthenticationForm):
'not_interactive': _('The selected OTP device is not interactive'),
'challenge_message': _('OTP Challenge: {0}'),
'invalid_token': _('Invalid token. Please make sure you have entered it correctly.'),
'n_failed_attempts': ungettext_lazy(
'n_failed_attempts': ngettext_lazy(
"Verification temporarily disabled because of %(failure_count)d failed attempt, please try again soon.",
"Verification temporarily disabled because of %(failure_count)d failed attempts, please try again soon.",
"failure_count"),
Expand Down Expand Up @@ -235,7 +233,7 @@ class OTPAuthenticationForm(OTPAuthenticationFormMixin, AuthenticationForm):
otp_challenge = forms.CharField(required=False)

def clean(self):
self.cleaned_data = super(OTPAuthenticationForm, self).clean()
self.cleaned_data = super().clean()
self.clean_otp(self.get_user())

return self.cleaned_data
Expand Down Expand Up @@ -282,13 +280,13 @@ def verify(request):
otp_challenge = forms.CharField(required=False)

def __init__(self, user, request=None, *args, **kwargs):
super(OTPTokenForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)

self.user = user
self.fields['otp_device'].choices = self.device_choices(user)

def clean(self):
super(OTPTokenForm, self).clean()
super().clean()

self.clean_otp(self.user)

Expand Down
4 changes: 1 addition & 3 deletions src/django_otp/middleware.py
@@ -1,5 +1,3 @@
from __future__ import absolute_import, division, print_function, unicode_literals

import functools

from django.utils.functional import SimpleLazyObject
Expand All @@ -12,7 +10,7 @@ def is_verified(user):
return user.otp_device is not None


class OTPMiddleware(object):
class OTPMiddleware:
"""
This must be installed after
:class:`~django.contrib.auth.middleware.AuthenticationMiddleware` and
Expand Down
14 changes: 2 additions & 12 deletions src/django_otp/models.py
@@ -1,7 +1,3 @@
from __future__ import absolute_import, division, print_function, unicode_literals

import six

from django.apps import apps
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
Expand Down Expand Up @@ -77,16 +73,10 @@ class Device(models.Model):

objects = DeviceManager()

class Meta(object):
class Meta:
abstract = True

def __str__(self):
if six.PY3:
return self.__unicode__()
else:
return self.__unicode__().encode('utf-8')

def __unicode__(self):
try:
user = self.user
except ObjectDoesNotExist:
Expand Down Expand Up @@ -256,7 +246,7 @@ def verify_is_allowed(self):
'failure_count': self.throttling_failure_count,
})

return super(ThrottlingMixin, self).verify_is_allowed()
return super().verify_is_allowed()

def throttle_reset(self, commit=True):
"""
Expand Down
15 changes: 2 additions & 13 deletions src/django_otp/oath.py
@@ -1,19 +1,8 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from hashlib import sha1
import hmac
from struct import pack
from time import time

import six


if six.PY3:
iterbytes = iter
else:
def iterbytes(buf):
return (ord(b) for b in buf)


def hotp(key, counter, digits=6):
"""
Expand Down Expand Up @@ -43,7 +32,7 @@ def hotp(key, counter, digits=6):
"""
msg = pack(b'>Q', counter)
hs = hmac.new(key, msg, sha1).digest()
hs = list(iterbytes(hs))
hs = list(iter(hs))

offset = hs[19] & 0x0f
bin_code = (hs[offset] & 0x7f) << 24 | hs[offset + 1] << 16 | hs[offset + 2] << 8 | hs[offset + 3]
Expand Down Expand Up @@ -87,7 +76,7 @@ def totp(key, step=30, t0=0, digits=6, drift=0):
return TOTP(key, step, t0, digits, drift).token()


class TOTP(object):
class TOTP:
"""
An alternate TOTP interface.
Expand Down
2 changes: 0 additions & 2 deletions src/django_otp/plugins/otp_email/admin.py
@@ -1,5 +1,3 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from django.contrib import admin
from django.contrib.admin.sites import AlreadyRegistered

Expand Down
8 changes: 2 additions & 6 deletions src/django_otp/plugins/otp_email/conf.py
@@ -1,11 +1,7 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from six import iteritems

import django.conf


class OTPEmailSettings(object):
class OTPEmailSettings:
"""
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
Expand All @@ -21,7 +17,7 @@ def __init__(self):
Loads our settings from django.conf.settings, applying defaults for any
that are omitted.
"""
for name, default in iteritems(self.defaults):
for name, default in self.defaults.items():
value = getattr(django.conf.settings, name, default)
setattr(self, name, value)

Expand Down
3 changes: 0 additions & 3 deletions src/django_otp/plugins/otp_email/migrations/0001_initial.py
@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models

Expand Down
5 changes: 1 addition & 4 deletions src/django_otp/plugins/otp_email/models.py
@@ -1,11 +1,8 @@
from __future__ import absolute_import, division, print_function, unicode_literals

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
Expand All @@ -15,7 +12,7 @@


def default_key():
return force_text(random_hex(20))
return random_hex(20)


def key_validator(value):
Expand Down
2 changes: 0 additions & 2 deletions src/django_otp/plugins/otp_email/tests.py
@@ -1,5 +1,3 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from django.core import mail
from django.db import IntegrityError
from django.test.utils import override_settings
Expand Down
6 changes: 2 additions & 4 deletions src/django_otp/plugins/otp_hotp/admin.py
@@ -1,5 +1,3 @@
from __future__ import absolute_import, division, print_function, unicode_literals

from django.conf.urls import url
from django.contrib import admin
from django.contrib.admin.sites import AlreadyRegistered
Expand Down Expand Up @@ -40,7 +38,7 @@ class HOTPDeviceAdmin(admin.ModelAdmin):
radio_fields = {'digits': admin.HORIZONTAL}

def get_queryset(self, request):
queryset = super(HOTPDeviceAdmin, self).get_queryset(request)
queryset = super().get_queryset(request)
queryset = queryset.select_related('user')

return queryset
Expand All @@ -67,7 +65,7 @@ def get_urls(self):
urls = [
url(r'^(?P<pk>\d+)/config/$', self.admin_site.admin_view(self.config_view), name='otp_hotp_hotpdevice_config'),
url(r'^(?P<pk>\d+)/qrcode/$', self.admin_site.admin_view(self.qrcode_view), name='otp_hotp_hotpdevice_qrcode'),
] + super(HOTPDeviceAdmin, self).get_urls()
] + super().get_urls()

return urls

Expand Down

0 comments on commit 22447c1

Please sign in to comment.