diff --git a/src/authentic2_auth_kerberos/authenticators.py b/src/authentic2_auth_kerberos/authenticators.py
index 83938ed..383a489 100644
--- a/src/authentic2_auth_kerberos/authenticators.py
+++ b/src/authentic2_auth_kerberos/authenticators.py
@@ -1,7 +1,10 @@
from django.utils.translation import gettext_noop
-from django import forms
+from django.shortcuts import render
+
+from authentic2 import constants, utils
+
+from . import app_settings
-from . import app_settings, utils
class KerberosAuthenticator(object):
def enabled(self):
@@ -13,14 +16,15 @@ class KerberosAuthenticator(object):
def id(self):
return 'kerberos'
- def form(self):
- return forms.Form
-
- def post(self, request, form, nonce, next):
- return utils.redirect_next(request, 'kerberos-login', nonce=nonce)
-
- def template(self):
- return 'authentic2_auth_kerberos/login.html'
-
- def is_hidden(self, request):
- return 'a2_just_logged_out' not in request.COOKIES
+ def login(self, request, *args, **kwargs):
+ context = kwargs.get('context', {})
+ # autologin is allowed if :
+ # * a Kerberos login has already happened on this terminal
+ # * we do not come from the logout view
+ if (request.COOKIES.get('a2_kerberos_ok') == '1'
+ and 'a2_just_logged_out' not in request.COOKIES):
+ context['autologin'] = True
+ submit = request.method == 'POST' and 'login-kerberos' in request.POST
+ if submit:
+ return utils.redirect_to_login(request, login_url='kerberos-login')
+ return render(request, 'authentic2_auth_kerberos/login.html', context)
diff --git a/src/authentic2_auth_kerberos/templates/authentic2_auth_kerberos/login.html b/src/authentic2_auth_kerberos/templates/authentic2_auth_kerberos/login.html
index fd6d39f..71d5cc5 100644
--- a/src/authentic2_auth_kerberos/templates/authentic2_auth_kerberos/login.html
+++ b/src/authentic2_auth_kerberos/templates/authentic2_auth_kerberos/login.html
@@ -1,11 +1,13 @@
{% load i18n %}
- {% include "authentic2_auth_kerberos/autologin.html" %}
-
diff --git a/src/authentic2_auth_kerberos/utils.py b/src/authentic2_auth_kerberos/utils.py
deleted file mode 100644
index 35eeaac..0000000
--- a/src/authentic2_auth_kerberos/utils.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import urlparse
-import urllib
-
-from django.http import HttpResponseRedirect
-from django.core.urlresolvers import reverse
-
-def redirect_next(request, url, **kwargs):
- '''Redirect to URL, add or replace parameters with kwargs
-
- You can use relative or fully qualifier, or view names. You can provide
- view args and kwargs using named argument args and kwargs.
- '''
- # specialize on the next url parameter as it is so frequent
- if 'next' in request.REQUEST:
- next_url = request.REQUEST.get('next')
- kwargs['next'] = next_url
- if not url.startswith('/') and not url.startswith('http:') and not url.startswith('https:'):
- view_args = kwargs.pop('args', None)
- view_kwargs = kwargs.pop('kwargs', None)
- url = reverse(url, args=view_args, kwargs=view_kwargs)
- parsed = urlparse.urlparse(url)
- params = urlparse.parse_qs(parsed.query)
- for key in kwargs.keys():
- if kwargs[key] is None:
- del kwargs[key]
- params.update(kwargs)
- url = urlparse.urlunparse(parsed[:4] + (urllib.urlencode(params),) + parsed[5:])
- return HttpResponseRedirect(url)
diff --git a/src/authentic2_auth_kerberos/views.py b/src/authentic2_auth_kerberos/views.py
index f2722dd..330d6de 100644
--- a/src/authentic2_auth_kerberos/views.py
+++ b/src/authentic2_auth_kerberos/views.py
@@ -2,17 +2,28 @@ import logging
from django_kerberos.views import NegotiateView
-from authentic2.models import AuthenticationEvent
from authentic2 import utils
-__ALL_ = [ 'login' ]
+__ALL_ = ['login']
+
class A2NegotiateView(NegotiateView):
+ authentication_successful = False
+
def __init__(self, *args, **kwargs):
super(A2NegotiateView, self).__init__(*args, **kwargs)
self.logger = logging.getLogger(__name__)
def login_user(self, request, user):
+ self.authentication_successful = True
utils.login(request, user, 'kerberos')
+ def principal_valid(self, request, *args, **kwargs):
+ response = super(A2NegotiateView, self).principal_valid(request, *args, **kwargs)
+ if self.authentication_successful:
+ # set cookie so that automatic login will be tried next time
+ response.set_cookie('a2_kerberos_ok', '1', max_age=86400 * 365)
+ return response
+
+
login = A2NegotiateView.as_view()
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..3e6ec29
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+# authentic2-auth-kerberos - Kerberos plugin for authentic2
+# Copyright (C) 2010-2019 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+
+import pytest
+import django_webtest
+
+from authentic2.custom_user.models import User
+from authentic2.a2_rbac.utils import get_default_ou
+
+
+@pytest.fixture
+def app_factory(settings, tmpdir):
+ settings.MEDIA_ROOT = str(tmpdir.mkdir('media'))
+ wtm = django_webtest.WebTestMixin()
+ wtm._patch_settings()
+ try:
+ def factory(hostname='localhost'):
+ return django_webtest.DjangoTestApp(extra_environ={'HTTP_HOST': hostname})
+ yield factory
+ finally:
+ wtm._unpatch_settings()
+
+
+@pytest.fixture
+def app(app_factory):
+ return app_factory()
+
+
+@pytest.fixture
+def create_user():
+ def func(**kwargs):
+ password = kwargs.pop('password', None) or kwargs['username']
+ user, created = User.objects.get_or_create(**kwargs)
+ if password:
+ user.set_password(password)
+ user.save()
+ return user
+ return func
diff --git a/tests/settings.py b/tests/settings.py
index 2a167b4..fbe641a 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -14,3 +14,5 @@ if 'postgres' in DATABASES['default']['ENGINE']:
for key in ('PGPORT', 'PGHOST', 'PGUSER', 'PGPASSWORD'):
if key in os.environ:
DATABASES['default'][key[2:]] = os.environ[key]
+
+ALLOWED_HOSTS = ['localhost']
diff --git a/tests/test_login.py b/tests/test_login.py
new file mode 100644
index 0000000..69e8167
--- /dev/null
+++ b/tests/test_login.py
@@ -0,0 +1,76 @@
+# authentic2-auth-kerberos - Kerberos plugin for authentic2
+# Copyright (C) 2010-2019 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+import pytest
+
+from authentic2.custom_user.models import User
+
+
+@pytest.fixture(autouse=True)
+def kerberos(monkeypatch):
+ import django_kerberos.views
+
+ class MockKerberos(object):
+ server_init_result = 1
+ server_step_result = 1
+ server_response_result = 'x'
+ principal = 'user@REALM'
+
+ def set(self, **kwargs):
+ self.__dict__ = kwargs
+
+ def authGSSServerInit(self, service):
+ return self.server_init_result, {}
+
+ def authGSSServerStep(self, context, authstr):
+ return self.server_step_result
+
+ def authGSSServerResponse(self, context):
+ return self.server_response_result
+
+ def authGSSServerUserName(self, context):
+ return self.principal
+
+ def authGSSServerClean(self, context):
+ pass
+
+ class KrbError(Exception):
+ pass
+ monkeypatch.setattr('django_kerberos.views.kerberos', MockKerberos())
+ return django_kerberos.views.kerberos
+
+
+def test_default(settings, app, db):
+ settings.A2_AUTH_KERBEROS_DJANGO_BACKEND = True
+
+ assert User.objects.count() == 0
+ assert 'a2_kerberos_ok' not in app.cookies
+ response = app.get('/login/')
+ assert 'login-kerberos' in response.text
+ assert 'autologin' not in response.text
+ response = response.forms['kerberos-form'].submit(name='login-kerberos')
+ assert response.location == '/accounts/kerberos/login/'
+ response = response.follow(headers={'Authorization': 'Negotiate y'})
+ assert app.cookies['a2_kerberos_ok'] == '1'
+ assert response.location == '/'
+ assert User.objects.count() == 1
+ assert User.objects.get(username='user@realm')
+
+ # logout
+ app.session.flush()
+ response = app.get('/login/')
+ assert 'login-kerberos' in response.text
+ assert 'autologin' in response.text
diff --git a/tox.ini b/tox.ini
index 49cb59f..b9d6436 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,7 +6,7 @@
[tox]
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/authentic2-auth-kerberos/
-envlist = py27-coverage-{dj18,dj111}-{oldldap,},pylint
+envlist = py27-coverage-dj111-{oldldap,},pylint
[testenv]
whitelist_externals =
@@ -42,6 +42,7 @@ deps =
ldaptools
http://git.entrouvert.org/authentic.git/snapshot/authentic-master.tar.bz2
oldldap: python-ldap<3
+ django-webtest
commands =
./getlasso.sh
py.test {env:COVERAGE:} {env:JUNIT:} {posargs:tests}