aboutsummaryrefslogtreecommitdiff
path: root/okupy
diff options
context:
space:
mode:
authorMichał Górny <mgorny@gentoo.org>2013-08-08 16:01:11 +0200
committerMichał Górny <mgorny@gentoo.org>2013-08-09 22:39:53 +0200
commit29b63498ad9db38f268aa29a3fcc0af1b9806e1a (patch)
tree47302a64b4b5492b9b7534989221cd8469853eab /okupy
parentMerge pull request #49 from mgorny/merged-settings (diff)
downloadidentity.gentoo.org-29b63498ad9db38f268aa29a3fcc0af1b9806e1a.tar.gz
identity.gentoo.org-29b63498ad9db38f268aa29a3fcc0af1b9806e1a.tar.bz2
identity.gentoo.org-29b63498ad9db38f268aa29a3fcc0af1b9806e1a.zip
Introduce initial code for two-phase auth support.
This commit adds a simple NoOTPDevice model that currently serves the purpose of responding successfully to any request. The login view has been extended with proper OTP device setup and initial verification support.
Diffstat (limited to 'okupy')
-rw-r--r--okupy/accounts/views.py13
-rw-r--r--okupy/otp/__init__.py28
-rw-r--r--okupy/otp/nootp/__init__.py0
-rw-r--r--okupy/otp/nootp/models.py11
-rw-r--r--okupy/settings/__init__.py2
-rw-r--r--okupy/tests/settings.py3
-rw-r--r--okupy/tests/unit/views.py17
7 files changed, 64 insertions, 10 deletions
diff --git a/okupy/accounts/views.py b/okupy/accounts/views.py
index f57b1fb..66e8c1c 100644
--- a/okupy/accounts/views.py
+++ b/okupy/accounts/views.py
@@ -4,7 +4,6 @@ from django.conf import settings
from django.contrib import messages
from django.contrib.auth import (login as _login, logout as _logout,
authenticate)
-from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.db import IntegrityError
@@ -15,6 +14,7 @@ from django.shortcuts import redirect, render
from django.utils.html import format_html
from django.utils.http import urlencode
from django.views.decorators.csrf import csrf_exempt
+from django_otp.decorators import otp_required
from openid.extensions.ax import FetchRequest, FetchResponse
from openid.extensions.sreg import SRegRequest, SRegResponse
@@ -31,6 +31,7 @@ from .openid_store import DjangoDBOpenIDStore
from ..common.ldap_helpers import get_ldap_connection
from ..common.exceptions import OkupyError
from ..common.log import log_extra_data
+from ..otp import init_otp
# the following two are for exceptions
import openid.yadis.discover
@@ -56,7 +57,7 @@ class DevListsView(View):
return render(request, self.template_name, {'devlist': devlist})
-@login_required
+@otp_required
def index(request):
anon_ldap_user = get_ldap_connection()
results = anon_ldap_user.search_s(settings.AUTH_LDAP_USER_DN_TEMPLATE % {
@@ -151,8 +152,12 @@ def login(request):
if user and user.is_active:
_login(request, user)
- if request.user.is_authenticated():
+ # prepare devices, and see if OTP is enabled
+ init_otp(request)
+ if request.user.is_verified():
return redirect(next)
+ if request.user.is_authenticated():
+ raise NotImplementedError('OTP form not implemented yet')
if login_form is None:
login_form = LoginForm()
@@ -431,7 +436,7 @@ openid_ax_attribute_mapping = {
}
-@login_required
+@otp_required
def openid_auth_site(request):
try:
oreq = request.session['openid_request']
diff --git a/okupy/otp/__init__.py b/okupy/otp/__init__.py
new file mode 100644
index 0000000..810a2e3
--- /dev/null
+++ b/okupy/otp/__init__.py
@@ -0,0 +1,28 @@
+# vim:fileencoding=utf8:et:ts=4:sts=4:sw=4:ft=python
+
+from django_otp import login as otp_login
+from django_otp.middleware import OTPMiddleware
+
+from .nootp.models import NoOTPDevice
+
+def init_otp(request):
+ """
+ Initialize OTP after login. This sets up OTP devices
+ for django_otp and calls the middleware to fill
+ request.user.is_verified().
+ """
+
+ nodev, created = NoOTPDevice.objects.get_or_create(
+ user=request.user,
+ defaults={
+ 'name': 'OTP-disabled pass-through',
+ })
+ if created:
+ nodev.save()
+
+ # nootp may match already
+ if nodev.verify_token():
+ otp_login(request, nodev)
+
+ # add .is_verified()
+ OTPMiddleware().process_request(request)
diff --git a/okupy/otp/nootp/__init__.py b/okupy/otp/nootp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/okupy/otp/nootp/__init__.py
diff --git a/okupy/otp/nootp/models.py b/okupy/otp/nootp/models.py
new file mode 100644
index 0000000..0a5b0af
--- /dev/null
+++ b/okupy/otp/nootp/models.py
@@ -0,0 +1,11 @@
+# vim:fileencoding=utf8:et:ts=4:sts=4:sw=4:ft=python
+
+from django_otp.models import Device
+
+class NoOTPDevice(Device):
+ """ A fake OTP device that successfully verifies token
+ if user has OTP disabled. """
+
+ def verify_token(self, token=None):
+ # TODO: put some real code
+ return True
diff --git a/okupy/settings/__init__.py b/okupy/settings/__init__.py
index da2a240..eaa496e 100644
--- a/okupy/settings/__init__.py
+++ b/okupy/settings/__init__.py
@@ -141,6 +141,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
@@ -155,6 +156,7 @@ INSTALLED_APPS = (
'django.contrib.messages',
'django.contrib.staticfiles',
'okupy.accounts',
+ 'okupy.otp.nootp',
)
#Compressor settings
diff --git a/okupy/tests/settings.py b/okupy/tests/settings.py
index c6e625d..a002253 100644
--- a/okupy/tests/settings.py
+++ b/okupy/tests/settings.py
@@ -139,6 +139,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
@@ -159,7 +160,9 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'django_otp',
'okupy.accounts',
+ 'okupy.otp.nootp',
'okupy.tests'
)
diff --git a/okupy/tests/unit/views.py b/okupy/tests/unit/views.py
index a564170..0eeb5aa 100644
--- a/okupy/tests/unit/views.py
+++ b/okupy/tests/unit/views.py
@@ -5,13 +5,20 @@ from django.contrib.auth.models import AnonymousUser
from django.core.urlresolvers import resolve
from django.test import TestCase, RequestFactory
+from django_otp.middleware import OTPMiddleware
+
from ...accounts.views import login, index, signup
from ...accounts.forms import LoginForm
-class LoginViewTests(TestCase):
- request = RequestFactory().get('/login')
+def anon_request(uri):
+ request = RequestFactory().get(uri)
request.session = {}
request.user = AnonymousUser()
+ OTPMiddleware().process_request(request)
+ return request
+
+class LoginViewTests(TestCase):
+ request = anon_request('/login')
response = login(request)
def test_login_url_resolves_to_login_view(self):
@@ -25,8 +32,7 @@ class LoginViewTests(TestCase):
self.assertTemplateUsed('login.html')
class IndexViewTests(TestCase):
- request = RequestFactory().get('/')
- request.user = AnonymousUser()
+ request = anon_request('/')
response = index(request)
def test_index_url_resolves_to_index_view(self):
@@ -40,8 +46,7 @@ class IndexViewTests(TestCase):
self.assertTemplateUsed('index.html')
class SignupViewTests(TestCase):
- request = RequestFactory().get('/signup')
- request.user = AnonymousUser()
+ request = anon_request('/signup')
response = signup(request)
def test_signup_url_resolves_to_signup_view(self):