[Python-modules-commits] [python-django-otp] 01/09: Import python-django-otp_0.3.10.orig.tar.gz
Michael Fladischer
fladi at moszumanska.debian.org
Mon Mar 6 10:15:37 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 418274d75dc81df5f55be32a4250e98f3de35c3e
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Mon Mar 6 10:31:57 2017 +0100
Import python-django-otp_0.3.10.orig.tar.gz
---
CHANGES | 13 ++++
MANIFEST.in | 2 +
PKG-INFO | 3 +-
django_otp.egg-info/PKG-INFO | 3 +-
django_otp.egg-info/SOURCES.txt | 2 +
django_otp.egg-info/requires.txt | 2 +-
django_otp/plugins/otp_hotp/admin.py | 71 ++++++++++++++++++
django_otp/plugins/otp_hotp/models.py | 31 ++++++++
.../otp_hotp/templates/otp_hotp/admin/config.html | 16 ++++
django_otp/plugins/otp_hotp/tests.py | 28 +++++++
.../plugins/otp_static/migrations/0001_initial.py | 2 +-
django_otp/plugins/otp_static/models.py | 2 +-
django_otp/plugins/otp_totp/admin.py | 71 ++++++++++++++++++
django_otp/plugins/otp_totp/models.py | 30 ++++++++
.../otp_totp/templates/otp_totp/admin/config.html | 16 ++++
django_otp/plugins/otp_totp/tests.py | 38 +++++++---
docs/source/conf.py | 2 +-
docs/source/overview.rst | 87 +++++++++++++++-------
setup.py | 5 +-
19 files changed, 377 insertions(+), 47 deletions(-)
diff --git a/CHANGES b/CHANGES
index de303ef..433eec8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,16 @@
+v0.3.10 - March 4, 2017 - Built-in QR Code support
+--------------------------------------------------
+
+- `#20`_: Generate HOTP and TOTP otpauth URLs and corresponding QR Codes. You
+ need the `qrcode`_ package installed for this feature.
+
+- Support for Python 2.6 and Django 1.4 were dropped in this version (long
+ overdue).
+
+.. _#20: https://bitbucket.org/psagers/django-otp/issues/20/how-to-pair-from-the-admin
+.. _qrcode: https://pypi.python.org/pypi/qrcode/
+
+
v0.3.8 - November 27, 2016 - Forward compatbility for Django 2.0
----------------------------------------------------------------
diff --git a/MANIFEST.in b/MANIFEST.in
index 3caa31a..1f57ad3 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,4 +4,6 @@ recursive-include docs *.rst *.py Makefile
prune docs/build
recursive-include django_otp/plugins/otp_email/templates *
+recursive-include django_otp/plugins/otp_hotp/templates *
+recursive-include django_otp/plugins/otp_totp/templates *
recursive-include django_otp/templates *
diff --git a/PKG-INFO b/PKG-INFO
index c4e393f..0bcf992 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-otp
-Version: 0.3.8
+Version: 0.3.10
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
@@ -38,7 +38,6 @@ 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
diff --git a/django_otp.egg-info/PKG-INFO b/django_otp.egg-info/PKG-INFO
index c4e393f..0bcf992 100644
--- a/django_otp.egg-info/PKG-INFO
+++ b/django_otp.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-otp
-Version: 0.3.8
+Version: 0.3.10
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
@@ -38,7 +38,6 @@ 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
diff --git a/django_otp.egg-info/SOURCES.txt b/django_otp.egg-info/SOURCES.txt
index c20a3ba..a7841df 100644
--- a/django_otp.egg-info/SOURCES.txt
+++ b/django_otp.egg-info/SOURCES.txt
@@ -40,6 +40,7 @@ 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_hotp/templates/otp_hotp/admin/config.html
django_otp/plugins/otp_static/__init__.py
django_otp/plugins/otp_static/admin.py
django_otp/plugins/otp_static/lib.py
@@ -61,6 +62,7 @@ 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/plugins/otp_totp/templates/otp_totp/admin/config.html
django_otp/templates/otp/.DS_Store
django_otp/templates/otp/admin14/login.html
django_otp/templates/otp/admin15/login.html
diff --git a/django_otp.egg-info/requires.txt b/django_otp.egg-info/requires.txt
index 5137bb2..7232fe7 100644
--- a/django_otp.egg-info/requires.txt
+++ b/django_otp.egg-info/requires.txt
@@ -1 +1 @@
-django >= 1.4.2
+django >= 1.5.12
diff --git a/django_otp/plugins/otp_hotp/admin.py b/django_otp/plugins/otp_hotp/admin.py
index 49d040a..99be38e 100644
--- a/django_otp/plugins/otp_hotp/admin.py
+++ b/django_otp/plugins/otp_hotp/admin.py
@@ -1,7 +1,12 @@
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
+from django.core.urlresolvers import reverse
+from django.http import HttpResponse
+from django.template.response import TemplateResponse
+from django.utils.html import format_html
from .models import HOTPDevice
@@ -11,6 +16,8 @@ class HOTPDeviceAdmin(admin.ModelAdmin):
:class:`~django.contrib.admin.ModelAdmin` for
:class:`~django_otp.plugins.otp_hotp.models.HOTPDevice`.
"""
+ list_display = ['user', 'name', 'confirmed', 'qrcode_link']
+
fieldsets = [
('Identity', {
'fields': ['user', 'name', 'confirmed'],
@@ -21,10 +28,74 @@ class HOTPDeviceAdmin(admin.ModelAdmin):
('State', {
'fields': ['counter'],
}),
+ (None, {
+ 'fields': ['qrcode_link'],
+ }),
]
raw_id_fields = ['user']
+ readonly_fields = ['qrcode_link']
radio_fields = {'digits': admin.HORIZONTAL}
+ def get_queryset(self, request):
+ queryset = super(HOTPDeviceAdmin, self).get_queryset(request)
+ queryset = queryset.select_related('user')
+
+ return queryset
+
+ #
+ # Columns
+ #
+
+ def qrcode_link(self, device):
+ if device is not None:
+ href = reverse('admin:otp_hotp_hotpdevice_config', kwargs={'pk': device.pk})
+ link = format_html('<a href="{}">qrcode</a>', href)
+ else:
+ link = ''
+
+ return link
+ qrcode_link.short_description = "QR Code"
+
+ #
+ # Custom views
+ #
+
+ 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()
+
+ return urls
+
+ def config_view(self, request, pk):
+ device = HOTPDevice.objects.get(pk=pk)
+
+ try:
+ context = dict(
+ self.admin_site.each_context(request),
+ device=device,
+ )
+ except AttributeError: # Older versions don't have each_context().
+ context = {'device': device}
+
+ return TemplateResponse(request, 'otp_hotp/admin/config.html', context)
+
+ def qrcode_view(self, request, pk):
+ device = HOTPDevice.objects.get(pk=pk)
+
+ try:
+ import qrcode
+ import qrcode.image.svg
+
+ img = qrcode.make(device.config_url, image_factory=qrcode.image.svg.SvgImage)
+ response = HttpResponse(content_type='image/svg+xml')
+ img.save(response)
+ except ImportError:
+ response = HttpResponse('', status=503)
+
+ return response
+
try:
admin.site.register(HOTPDevice, HOTPDeviceAdmin)
diff --git a/django_otp/plugins/otp_hotp/models.py b/django_otp/plugins/otp_hotp/models.py
index 8f553fa..ff8b49e 100644
--- a/django_otp/plugins/otp_hotp/models.py
+++ b/django_otp/plugins/otp_hotp/models.py
@@ -1,8 +1,12 @@
from __future__ import absolute_import, division, print_function, unicode_literals
+from base64 import b32encode
from binascii import unhexlify
+from django.conf import settings
from django.db import models
+from django.utils.six import string_types
+from django.utils.six.moves.urllib.parse import quote, urlencode
from django_otp.models import Device
from django_otp.oath import hotp
@@ -76,3 +80,30 @@ class HOTPDevice(Device):
verified = False
return verified
+
+ @property
+ def config_url(self):
+ """
+ A URL for configuring Google Authenticator or similar.
+
+ See https://github.com/google/google-authenticator/wiki/Key-Uri-Format.
+ The issuer is taken from :setting:`OTP_HOTP_ISSUER`, if available.
+
+ """
+ label = self.user.get_username()
+ params = {
+ 'secret': b32encode(self.bin_key),
+ 'algorithm': 'SHA1',
+ 'digits': self.digits,
+ 'counter': self.counter,
+ }
+
+ issuer = getattr(settings, 'OTP_HOTP_ISSUER', None)
+ if isinstance(issuer, string_types) and (issuer != ''):
+ issuer = issuer.replace(':', '')
+ params['issuer'] = issuer
+ label = '{}:{}'.format(issuer, label)
+
+ url = 'otpauth://hotp/{}?{}'.format(quote(label), urlencode(params))
+
+ return url
diff --git a/django_otp/plugins/otp_hotp/templates/otp_hotp/admin/config.html b/django_otp/plugins/otp_hotp/templates/otp_hotp/admin/config.html
new file mode 100644
index 0000000..145d726
--- /dev/null
+++ b/django_otp/plugins/otp_hotp/templates/otp_hotp/admin/config.html
@@ -0,0 +1,16 @@
+{% extends "admin/base_site.html" %}
+
+{% block content %}
+<div style="text-align: center;">
+ <p id="qrcode">
+ <img width="200" height="200"
+ src="{% url "admin:otp_hotp_hotpdevice_qrcode" pk=device.pk %}"
+ onerror="document.getElementById('qrcode').style.display = 'none'; document.getElementById('no-qrcode').style.display = 'block';"
+ >
+ </p>
+ <p id="no-qrcode" style="display: none">
+ Install <a href="https://pypi.python.org/pypi/qrcode/">qrcode</a> or use the URL below:
+ </p>
+ <p>{{ device.config_url }}</p>
+</div>
+{% endblock %}
diff --git a/django_otp/plugins/otp_hotp/tests.py b/django_otp/plugins/otp_hotp/tests.py
index 5660dee..45e5e93 100644
--- a/django_otp/plugins/otp_hotp/tests.py
+++ b/django_otp/plugins/otp_hotp/tests.py
@@ -1,6 +1,8 @@
from __future__ import absolute_import, division, print_function, unicode_literals
from django.db import IntegrityError
+from django.test.utils import override_settings
+from django.utils.six.moves.urllib.parse import urlsplit, parse_qs
from django_otp.tests import TestCase
@@ -43,3 +45,29 @@ class HOTPTest(TestCase):
self.assertTrue(not ok)
self.assertEqual(self.device.counter, 0)
+
+ def test_config_url_no_issuer(self):
+ with override_settings(OTP_HOTP_ISSUER=None):
+ url = self.device.config_url
+
+ parsed = urlsplit(url)
+ params = parse_qs(parsed.query)
+
+ self.assertEqual(parsed.scheme, 'otpauth')
+ self.assertEqual(parsed.netloc, 'hotp')
+ self.assertEqual(parsed.path, '/alice')
+ self.assertIn('secret', params)
+ self.assertNotIn('issuer', params)
+
+ def test_config_url_issuer(self):
+ with override_settings(OTP_HOTP_ISSUER='example.com'):
+ url = self.device.config_url
+
+ parsed = urlsplit(url)
+ params = parse_qs(parsed.query)
+
+ self.assertEqual(parsed.scheme, 'otpauth')
+ self.assertEqual(parsed.netloc, 'hotp')
+ self.assertEqual(parsed.path, '/example.com%3Aalice')
+ self.assertIn('secret', params)
+ self.assertIn('issuer', params)
diff --git a/django_otp/plugins/otp_static/migrations/0001_initial.py b/django_otp/plugins/otp_static/migrations/0001_initial.py
index cc58165..2368af6 100644
--- a/django_otp/plugins/otp_static/migrations/0001_initial.py
+++ b/django_otp/plugins/otp_static/migrations/0001_initial.py
@@ -30,7 +30,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('token', models.CharField(max_length=16, db_index=True)),
- ('device', models.ForeignKey(related_name='token_set', to='otp_static.StaticDevice')),
+ ('device', models.ForeignKey(related_name='token_set', to='otp_static.StaticDevice', on_delete=models.CASCADE)),
],
options={
},
diff --git a/django_otp/plugins/otp_static/models.py b/django_otp/plugins/otp_static/models.py
index 8a5b075..6069d9e 100644
--- a/django_otp/plugins/otp_static/models.py
+++ b/django_otp/plugins/otp_static/models.py
@@ -45,7 +45,7 @@ class StaticToken(models.Model):
*CharField*: A random string up to 16 characters.
"""
- device = models.ForeignKey(StaticDevice, related_name='token_set')
+ device = models.ForeignKey(StaticDevice, related_name='token_set', on_delete=models.CASCADE)
token = models.CharField(max_length=16, db_index=True)
@staticmethod
diff --git a/django_otp/plugins/otp_totp/admin.py b/django_otp/plugins/otp_totp/admin.py
index 2293bb1..003ca91 100644
--- a/django_otp/plugins/otp_totp/admin.py
+++ b/django_otp/plugins/otp_totp/admin.py
@@ -1,7 +1,12 @@
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
+from django.core.urlresolvers import reverse
+from django.http import HttpResponse
+from django.template.response import TemplateResponse
+from django.utils.html import format_html
from .models import TOTPDevice
@@ -11,6 +16,8 @@ class TOTPDeviceAdmin(admin.ModelAdmin):
:class:`~django.contrib.admin.ModelAdmin` for
:class:`~django_otp.plugins.otp_totp.models.TOTPDevice`.
"""
+ list_display = ['user', 'name', 'confirmed', 'qrcode_link']
+
fieldsets = [
('Identity', {
'fields': ['user', 'name', 'confirmed'],
@@ -21,10 +28,74 @@ class TOTPDeviceAdmin(admin.ModelAdmin):
('State', {
'fields': ['drift'],
}),
+ (None, {
+ 'fields': ['qrcode_link'],
+ }),
]
raw_id_fields = ['user']
+ readonly_fields = ['qrcode_link']
radio_fields = {'digits': admin.HORIZONTAL}
+ def get_queryset(self, request):
+ queryset = super(TOTPDeviceAdmin, self).get_queryset(request)
+ queryset = queryset.select_related('user')
+
+ return queryset
+
+ #
+ # Columns
+ #
+
+ def qrcode_link(self, device):
+ if device is not None:
+ href = reverse('admin:otp_totp_totpdevice_config', kwargs={'pk': device.pk})
+ link = format_html('<a href="{}">qrcode</a>', href)
+ else:
+ link = ''
+
+ return link
+ qrcode_link.short_description = "QR Code"
+
+ #
+ # Custom views
+ #
+
+ def get_urls(self):
+ urls = [
+ url(r'^(?P<pk>\d+)/config/$', self.admin_site.admin_view(self.config_view), name='otp_totp_totpdevice_config'),
+ url(r'^(?P<pk>\d+)/qrcode/$', self.admin_site.admin_view(self.qrcode_view), name='otp_totp_totpdevice_qrcode'),
+ ] + super(TOTPDeviceAdmin, self).get_urls()
+
+ return urls
+
+ def config_view(self, request, pk):
+ device = TOTPDevice.objects.get(pk=pk)
+
+ try:
+ context = dict(
+ self.admin_site.each_context(request),
+ device=device,
+ )
+ except AttributeError: # Older versions don't have each_context().
+ context = {'device': device}
+
+ return TemplateResponse(request, 'otp_totp/admin/config.html', context)
+
+ def qrcode_view(self, request, pk):
+ device = TOTPDevice.objects.get(pk=pk)
+
+ try:
+ import qrcode
+ import qrcode.image.svg
+
+ img = qrcode.make(device.config_url, image_factory=qrcode.image.svg.SvgImage)
+ response = HttpResponse(content_type='image/svg+xml')
+ img.save(response)
+ except ImportError:
+ response = HttpResponse('', status=503)
+
+ return response
+
try:
admin.site.register(TOTPDevice, TOTPDeviceAdmin)
diff --git a/django_otp/plugins/otp_totp/models.py b/django_otp/plugins/otp_totp/models.py
index e7dbf1b..e001538 100644
--- a/django_otp/plugins/otp_totp/models.py
+++ b/django_otp/plugins/otp_totp/models.py
@@ -1,11 +1,14 @@
from __future__ import absolute_import, division, print_function, unicode_literals
+from base64 import b32encode
from binascii import unhexlify
import time
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
from django_otp.models import Device
from django_otp.oath import TOTP
@@ -106,3 +109,30 @@ class TOTPDevice(Device):
self.save()
return verified
+
+ @property
+ def config_url(self):
+ """
+ A URL for configuring Google Authenticator or similar.
+
+ See https://github.com/google/google-authenticator/wiki/Key-Uri-Format.
+ The issuer is taken from :setting:`OTP_TOTP_ISSUER`, if available.
+
+ """
+ label = self.user.get_username()
+ params = {
+ 'secret': b32encode(self.bin_key),
+ 'algorithm': 'SHA1',
+ 'digits': self.digits,
+ 'period': self.step,
+ }
+
+ issuer = getattr(settings, 'OTP_TOTP_ISSUER', None)
+ if isinstance(issuer, string_types) and (issuer != ''):
+ issuer = issuer.replace(':', '')
+ params['issuer'] = issuer
+ label = '{}:{}'.format(issuer, label)
+
+ url = 'otpauth://totp/{}?{}'.format(quote(label), urlencode(params))
+
+ return url
diff --git a/django_otp/plugins/otp_totp/templates/otp_totp/admin/config.html b/django_otp/plugins/otp_totp/templates/otp_totp/admin/config.html
new file mode 100644
index 0000000..3db59b2
--- /dev/null
+++ b/django_otp/plugins/otp_totp/templates/otp_totp/admin/config.html
@@ -0,0 +1,16 @@
+{% extends "admin/base_site.html" %}
+
+{% block content %}
+<div style="text-align: center;">
+ <p id="qrcode">
+ <img width="200" height="200"
+ src="{% url "admin:otp_totp_totpdevice_qrcode" pk=device.pk %}"
+ onerror="document.getElementById('qrcode').style.display = 'none'; document.getElementById('no-qrcode').style.display = 'block';"
+ >
+ </p>
+ <p id="no-qrcode" style="display: none">
+ Install <a href="https://pypi.python.org/pypi/qrcode/">qrcode</a> or use the URL below:
+ </p>
+ <p>{{ device.config_url }}</p>
+</div>
+{% endblock %}
diff --git a/django_otp/plugins/otp_totp/tests.py b/django_otp/plugins/otp_totp/tests.py
index 6804f46..1c7b919 100644
--- a/django_otp/plugins/otp_totp/tests.py
+++ b/django_otp/plugins/otp_totp/tests.py
@@ -3,16 +3,8 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from time import time
from django.db import IntegrityError
-
-try:
- from django.test.utils import override_settings
-except ImportError:
- # Django < 1.4 doesn't have override_settings. Just skip the tests in that
- # case.
- from django.utils.unittest import skip
-
- def override_settings(*args, **kwargs):
- return skip
+from django.test.utils import override_settings
+from django.utils.six.moves.urllib.parse import urlsplit, parse_qs
from django_otp.tests import TestCase
@@ -75,3 +67,29 @@ class TOTPTest(TestCase):
self.assertEqual(self.device.last_t, 3)
self.assertTrue(verified1)
self.assertFalse(verified2)
+
+ def test_config_url(self):
+ with override_settings(OTP_TOTP_ISSUER=None):
+ url = self.device.config_url
+
+ parsed = urlsplit(url)
+ params = parse_qs(parsed.query)
+
+ self.assertEqual(parsed.scheme, 'otpauth')
+ self.assertEqual(parsed.netloc, 'totp')
+ self.assertEqual(parsed.path, '/alice')
+ self.assertIn('secret', params)
+ self.assertNotIn('issuer', params)
+
+ def test_config_url_issuer(self):
+ with override_settings(OTP_TOTP_ISSUER='example.com'):
+ url = self.device.config_url
+
+ parsed = urlsplit(url)
+ params = parse_qs(parsed.query)
+
+ self.assertEqual(parsed.scheme, 'otpauth')
+ self.assertEqual(parsed.netloc, 'totp')
+ self.assertEqual(parsed.path, '/example.com%3Aalice')
+ self.assertIn('secret', params)
+ self.assertIn('issuer', params)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 24a7a03..6ef1601 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -75,7 +75,7 @@ copyright = u'2012, Peter Sagerson'
# The short X.Y version.
version = '0.3'
# The full version, including alpha/beta/rc tags.
-release = '0.3.8'
+release = '0.3.10'
# 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 fb43b0d..96b26a0 100644
--- a/docs/source/overview.rst
+++ b/docs/source/overview.rst
@@ -172,11 +172,10 @@ are subclassed from :class:`django_otp.models.Device`. Each model class supports
a single type of OTP device. Remember that when we use the term :term:`device`
in this context, we're not necessarily referring to a physical device. At the
code level, a device is a model object that can verify a particular type of OTP.
-For example, you might have a `YubiKey <http://www.yubico.com/yubikey>`_ that
-supports both the Yubico OTP algorithm and the HOTP standard: these would be
-represented as different devices and likely served by different plugins. A
-device that delivered HOTP values to a user by SMS would be a third device
-defined by another plugin.
+For example, you might have a `YubiKey`_ that supports both the Yubico OTP
+algorithm and the HOTP standard: these would be represented as different devices
+and likely served by different plugins. A device that delivered HOTP values to a
+user by SMS would be a third device defined by another plugin.
OTP plugins are distributed as Django apps; to install a plugin, just add it to
:setting:`INSTALLED_APPS` like any other. The order can be significant: any time
@@ -200,6 +199,9 @@ unconfirmed initially. Unconfirmed devices will be ignored by the high-level OTP
APIs.
+.. _YubiKey: http://www.yubico.com/yubikey
+
+
.. _built-in-plugins:
Built-in Plugins
@@ -209,19 +211,24 @@ django-otp includes support for several standard device types.
:class:`~django_otp.plugins.otp_hotp.models.HOTPDevice` and
:class:`~django_otp.plugins.otp_totp.models.TOTPDevice` handle standard OTP
algorithms, which can be used with a variety of OTP generators. For example,
-it's easy to pair these devices with `Google Authenticator
-<https://github.com/google/google-authenticator>`_ using the `otpauth URL scheme
-<https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_.
+it's easy to pair these devices with `Google Authenticator`_ using the `otpauth
+URL scheme`_. If you have the `qrcode`_ package installed, the admin interface
+will generate QR Codes for you.
+
+
+.. _Google Authenticator: https://github.com/google/google-authenticator
+.. _otpauth URL scheme: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+.. _qrcode: https://pypi.python.org/pypi/qrcode/
HOTP Devices
++++++++++++
-`HOTP <http://tools.ietf.org/html/rfc4226#section-5>`_ is an algorithm that
-generates a pseudo-random sequence of codes based on an incrementing counter.
-Every time a prover generates a new code or a verifier verifies one, they
-increment their respective counters. This algorithm will fail if the prover
-generates too many codes without a successful verification.
+`HOTP`_ is an algorithm that generates a pseudo-random sequence of codes based
+on an incrementing counter. Every time a prover generates a new code or a
+verifier verifies one, they increment their respective counters. This algorithm
+will fail if the prover generates too many codes without a successful
+verification.
.. module:: django_otp.plugins.otp_hotp
@@ -230,15 +237,28 @@ generates too many codes without a successful verification.
.. autoclass:: django_otp.plugins.otp_hotp.admin.HOTPDeviceAdmin
+.. _HOTP: http://tools.ietf.org/html/rfc4226#section-5
+
+HOTP Settings
+'''''''''''''
+
+**OTP_HOTP_ISSUER**
+
+.. setting:: OTP_HOTP_ISSUER
+
+Default: ``None``
+
+The ``issuer`` parameter for the otpauth URL generated by
+:attr:`~django_otp.plugins.otp_hotp.models.HOTPDevice.config_url`.
+
TOTP Devices
++++++++++++
-`TOTP <http://tools.ietf.org/html/rfc6238#section-4>`_ is an algorithm that
-generates a pseudo-random sequence of codes based on the current time. A typical
-implementation will change codes every 30 seconds, although this is
-configurable. This algorithm will fail if the prover and verifier have clocks
-that drift too far apart.
+`TOTP`_ is an algorithm that generates a pseudo-random sequence of codes based
+on the current time. A typical implementation will change codes every 30
+seconds, although this is configurable. This algorithm will fail if the prover
+and verifier have clocks that drift too far apart.
.. module:: django_otp.plugins.otp_totp
@@ -247,9 +267,21 @@ that drift too far apart.
.. autoclass:: django_otp.plugins.otp_totp.admin.TOTPDeviceAdmin
+.. _TOTP: http://tools.ietf.org/html/rfc6238#section-4
+
TOTP Settings
'''''''''''''
+**OTP_TOTP_ISSUER**
+
+.. setting:: OTP_TOTP_ISSUER
+
+Default: ``None``
+
+The ``issuer`` parameter for the otpauth URL generated by
+:attr:`~django_otp.plugins.otp_totp.models.TOTPDevice.config_url`.
+
+
**OTP_TOTP_SYNC**
.. setting:: OTP_TOTP_SYNC
@@ -300,10 +332,8 @@ Other Plugins
The framework author also maintains a couple of other plugins for less common
devices. Third-party plugins are not listed here.
- - `django-otp-yubikey <http://pypi.python.org/pypi/django-otp-yubikey>`_
- supports YubiKey USB devices.
- - `django-otp-twilio <http://pypi.python.org/pypi/django-otp-twilio>`_
- supports delivering OTPs via Twilio's SMS service.
+ - `django-otp-yubikey`_ supports YubiKey USB devices.
+ - `django-otp-twilio`_ supports delivering OTPs via Twilio's SMS service.
Settings
@@ -364,7 +394,12 @@ Glossary
.. rubric:: Footnotes
.. [#agents] If you'd like the second factor to persist across sessions, see
- `django-agent-trust <http://pypi.python.org/pypi/django-agent-trust>`_ and
- `django-otp-agents <http://pypi.python.org/pypi/django-otp-agents>`_. The
- former deals with assigning trust to user agents (i.e. browsers) across
- sessions and the latter includes tools to use OTPs to establish that trust.
+ `django-agent-trust`_ and `django-otp-agents`_. The former deals with
+ assigning trust to user agents (i.e. browsers) across sessions and the latter
+ includes tools to use OTPs to establish that trust.
+
+
+.. _django-agent-trust: http://pypi.python.org/pypi/django-agent-trust
+.. _django-otp-agents: http://pypi.python.org/pypi/django-otp-agents
+.. _django-otp-yubikey: http://pypi.python.org/pypi/django-otp-yubikey
+.. _django-otp-twilio: http://pypi.python.org/pypi/django-otp-twilio
diff --git a/setup.py b/setup.py
index 3d9978d..8c24d6d 100755
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
setup(
name='django-otp',
- version='0.3.8',
+ version='0.3.10',
description='A pluggable framework for adding two-factor authentication to Django using one-time passwords.',
long_description=open('README.rst').read(),
author='Peter Sagerson',
@@ -16,7 +16,7 @@ setup(
url='https://bitbucket.org/psagers/django-otp',
license='BSD',
install_requires=[
- 'django >= 1.4.2'
+ 'django >= 1.5.12'
],
classifiers=[
"Development Status :: 5 - Production/Stable",
@@ -24,7 +24,6 @@ setup(
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3",
--
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