enable autologin after first manual login (#30018)

This commit is contained in:
Benjamin Dauvergne 2019-08-22 17:16:56 +02:00
parent 38ceef6eb4
commit 839620aeee
8 changed files with 169 additions and 48 deletions

View File

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

View File

@ -1,11 +1,13 @@
{% load i18n %}
<div>
{% include "authentic2_auth_kerberos/autologin.html" %}
<form method="post">
{% if autologin %}
{% include "authentic2_auth_kerberos/autologin.html" %}
{% endif %}
<form id='kerberos-form' method="post">
{% csrf_token %}
<input type="submit" name="{{ submit_name }}" value="{% trans "Login" %}"/>
<button name="login-kerberos" value="1">{% trans "Login" %}</button>
{% if cancel %}
<input type="submit" name="cancel" value="{% trans 'Cancel' %}"/>
<input type="submit" name="cancel" value="{% trans 'Cancel' %}"/>
{% endif %}
</form>
</div>

View File

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

View File

@ -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()

53
tests/conftest.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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']

76
tests/test_login.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

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