2017-05-12 16:55:18 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
2017-09-25 17:16:19 +02:00
|
|
|
from unittest import mock
|
2017-05-12 16:55:18 +02:00
|
|
|
|
2017-09-25 17:16:19 +02:00
|
|
|
from django.core import signing
|
|
|
|
from django.core.urlresolvers import reverse
|
|
|
|
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
|
|
|
|
from django.test import RequestFactory, TestCase
|
2017-05-12 16:55:18 +02:00
|
|
|
|
|
|
|
from kfet.forms import UserGroupForm
|
2017-09-21 23:39:27 +02:00
|
|
|
from kfet.models import Account
|
|
|
|
|
|
|
|
from . import KFET_GENERIC_TRIGRAMME, KFET_GENERIC_USERNAME
|
2017-09-25 17:16:19 +02:00
|
|
|
from .backends import AccountBackend, GenericBackend
|
|
|
|
from .middleware import TemporaryAuthMiddleware
|
|
|
|
from .models import GenericTeamToken
|
2017-09-21 23:39:27 +02:00
|
|
|
from .utils import get_kfet_generic_user
|
2017-09-25 17:16:19 +02:00
|
|
|
from .views import GenericLoginView
|
2017-05-12 16:55:18 +02:00
|
|
|
|
|
|
|
|
2017-09-25 17:16:19 +02:00
|
|
|
##
|
|
|
|
# Forms
|
|
|
|
##
|
|
|
|
|
2017-05-12 16:55:18 +02:00
|
|
|
class UserGroupFormTests(TestCase):
|
|
|
|
"""Test suite for UserGroupForm."""
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
# create user
|
|
|
|
self.user = User.objects.create(username="foo", password="foo")
|
|
|
|
|
|
|
|
# create some K-Fêt groups
|
|
|
|
prefix_name = "K-Fêt "
|
|
|
|
names = ["Group 1", "Group 2", "Group 3"]
|
|
|
|
self.kfet_groups = [
|
|
|
|
Group.objects.create(name=prefix_name+name)
|
|
|
|
for name in names
|
|
|
|
]
|
|
|
|
|
|
|
|
# create a non-K-Fêt group
|
|
|
|
self.other_group = Group.objects.create(name="Other group")
|
|
|
|
|
|
|
|
def test_choices(self):
|
|
|
|
"""Only K-Fêt groups are selectable."""
|
|
|
|
form = UserGroupForm(instance=self.user)
|
|
|
|
groups_field = form.fields['groups']
|
2017-05-14 22:19:25 +02:00
|
|
|
self.assertQuerysetEqual(
|
|
|
|
groups_field.queryset,
|
|
|
|
[repr(g) for g in self.kfet_groups],
|
|
|
|
ordered=False,
|
|
|
|
)
|
2017-05-12 16:55:18 +02:00
|
|
|
|
|
|
|
def test_keep_others(self):
|
|
|
|
"""User stays in its non-K-Fêt groups."""
|
|
|
|
user = self.user
|
|
|
|
|
|
|
|
# add user to a non-K-Fêt group
|
|
|
|
user.groups.add(self.other_group)
|
|
|
|
|
|
|
|
# add user to some K-Fêt groups through UserGroupForm
|
|
|
|
data = {
|
|
|
|
'groups': [group.pk for group in self.kfet_groups],
|
|
|
|
}
|
|
|
|
form = UserGroupForm(data, instance=user)
|
|
|
|
|
|
|
|
form.is_valid()
|
|
|
|
form.save()
|
2017-05-14 22:19:25 +02:00
|
|
|
self.assertQuerysetEqual(
|
|
|
|
user.groups.all(),
|
|
|
|
[repr(g) for g in [self.other_group] + self.kfet_groups],
|
|
|
|
ordered=False,
|
|
|
|
)
|
2017-09-21 23:39:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
class KFetGenericUserTests(TestCase):
|
|
|
|
|
|
|
|
def test_exists(self):
|
|
|
|
"""
|
|
|
|
The account is set up when app is ready, so it should exist.
|
|
|
|
"""
|
|
|
|
generic = Account.objects.get_generic()
|
|
|
|
self.assertEqual(generic.trigramme, KFET_GENERIC_TRIGRAMME)
|
|
|
|
self.assertEqual(generic.user.username, KFET_GENERIC_USERNAME)
|
|
|
|
self.assertEqual(get_kfet_generic_user(), generic.user)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Backends
|
|
|
|
##
|
|
|
|
|
|
|
|
class AccountBackendTests(TestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.request = RequestFactory().get('/')
|
|
|
|
|
|
|
|
def test_valid(self):
|
|
|
|
acc = Account(trigramme='000')
|
|
|
|
acc.change_pwd('valid')
|
|
|
|
acc.save({'username': 'user'})
|
|
|
|
|
|
|
|
auth = AccountBackend().authenticate(
|
|
|
|
self.request, kfet_password='valid')
|
|
|
|
|
|
|
|
self.assertEqual(auth, acc.user)
|
|
|
|
|
|
|
|
def test_invalid(self):
|
|
|
|
auth = AccountBackend().authenticate(
|
|
|
|
self.request, kfet_password='invalid')
|
|
|
|
self.assertIsNone(auth)
|
|
|
|
|
|
|
|
|
|
|
|
class GenericBackendTests(TestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.request = RequestFactory().get('/')
|
|
|
|
|
|
|
|
def test_valid(self):
|
|
|
|
token = GenericTeamToken.objects.create_token()
|
|
|
|
|
|
|
|
auth = GenericBackend().authenticate(
|
|
|
|
self.request, kfet_token=token.token)
|
|
|
|
|
|
|
|
self.assertEqual(auth, get_kfet_generic_user())
|
|
|
|
self.assertEqual(GenericTeamToken.objects.all().count(), 0)
|
|
|
|
|
|
|
|
def test_invalid(self):
|
|
|
|
auth = GenericBackend().authenticate(
|
|
|
|
self.request, kfet_token='invalid')
|
|
|
|
self.assertIsNone(auth)
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Views
|
|
|
|
##
|
|
|
|
|
|
|
|
class GenericLoginViewTests(TestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
2017-10-26 11:38:11 +02:00
|
|
|
patcher_messages = mock.patch('cof.signals.messages')
|
2017-09-25 17:16:19 +02:00
|
|
|
patcher_messages.start()
|
|
|
|
self.addCleanup(patcher_messages.stop)
|
|
|
|
|
|
|
|
user_acc = Account(trigramme='000')
|
|
|
|
user_acc.save({'username': 'user'})
|
|
|
|
self.user = user_acc.user
|
|
|
|
self.user.set_password('user')
|
|
|
|
self.user.save()
|
|
|
|
|
|
|
|
team_acc = Account(trigramme='100')
|
|
|
|
team_acc.save({'username': 'team'})
|
|
|
|
self.team = team_acc.user
|
|
|
|
self.team.set_password('team')
|
|
|
|
self.team.save()
|
|
|
|
self.team.user_permissions.add(
|
|
|
|
Permission.objects.get(
|
|
|
|
content_type__app_label='kfet', codename='is_team'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.url = reverse('kfet.login.generic')
|
|
|
|
self.generic_user = get_kfet_generic_user()
|
|
|
|
|
|
|
|
def test_url(self):
|
|
|
|
self.assertEqual(self.url, '/k-fet/login/generic')
|
|
|
|
|
|
|
|
def test_notoken_get(self):
|
|
|
|
"""
|
|
|
|
Send confirmation for user to emit POST request, instead of GET.
|
|
|
|
"""
|
|
|
|
self.client.login(username='team', password='team')
|
|
|
|
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
|
|
|
|
self.assertEqual(r.status_code, 200)
|
|
|
|
self.assertTemplateUsed(r, 'kfet/confirm_form.html')
|
|
|
|
|
|
|
|
def test_notoken_post(self):
|
|
|
|
"""
|
|
|
|
POST request without token in COOKIES sets a token and redirects to
|
|
|
|
logout url.
|
|
|
|
"""
|
|
|
|
self.client.login(username='team', password='team')
|
|
|
|
|
|
|
|
r = self.client.post(self.url)
|
|
|
|
|
|
|
|
self.assertRedirects(
|
|
|
|
r, '/logout?next={}'.format(self.url),
|
|
|
|
fetch_redirect_response=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_notoken_not_team(self):
|
|
|
|
"""
|
|
|
|
Logged in user must be a team user to initiate login as generic user.
|
|
|
|
"""
|
|
|
|
self.client.login(username='user', password='user')
|
|
|
|
|
|
|
|
# With GET.
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
self.assertRedirects(
|
|
|
|
r, '/login?next={}'.format(self.url),
|
|
|
|
fetch_redirect_response=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Also with POST.
|
|
|
|
r = self.client.post(self.url)
|
|
|
|
self.assertRedirects(
|
|
|
|
r, '/login?next={}'.format(self.url),
|
|
|
|
fetch_redirect_response=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
def _set_signed_cookie(self, client, key, value):
|
|
|
|
signed_value = signing.get_cookie_signer(salt=key).sign(value)
|
|
|
|
client.cookies.load({key: signed_value})
|
|
|
|
|
|
|
|
def _is_cookie_deleted(self, client, key):
|
|
|
|
try:
|
|
|
|
self.assertNotIn(key, client.cookies)
|
|
|
|
except AssertionError:
|
|
|
|
try:
|
|
|
|
cookie = client.cookies[key]
|
|
|
|
# It also can be emptied.
|
|
|
|
self.assertEqual(cookie.value, '')
|
|
|
|
self.assertEqual(
|
|
|
|
cookie['expires'], 'Thu, 01-Jan-1970 00:00:00 GMT')
|
|
|
|
self.assertEqual(cookie['max-age'], 0)
|
|
|
|
except AssertionError:
|
|
|
|
raise AssertionError("The cookie '%s' still exists." % key)
|
|
|
|
|
|
|
|
def test_withtoken_valid(self):
|
|
|
|
"""
|
|
|
|
The kfet generic user is logged in.
|
|
|
|
"""
|
|
|
|
token = GenericTeamToken.objects.create(token='valid')
|
|
|
|
self._set_signed_cookie(
|
|
|
|
self.client, GenericLoginView.TOKEN_COOKIE_NAME, 'valid')
|
|
|
|
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
|
|
|
|
self.assertRedirects(r, reverse('kfet.kpsul'))
|
|
|
|
self.assertEqual(r.wsgi_request.user, self.generic_user)
|
|
|
|
self._is_cookie_deleted(
|
|
|
|
self.client, GenericLoginView.TOKEN_COOKIE_NAME)
|
|
|
|
with self.assertRaises(GenericTeamToken.DoesNotExist):
|
|
|
|
token.refresh_from_db()
|
|
|
|
|
|
|
|
def test_withtoken_invalid(self):
|
|
|
|
"""
|
|
|
|
If token is invalid, delete it and try again.
|
|
|
|
"""
|
|
|
|
self._set_signed_cookie(
|
|
|
|
self.client, GenericLoginView.TOKEN_COOKIE_NAME, 'invalid')
|
|
|
|
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
|
|
|
|
self.assertRedirects(r, self.url, fetch_redirect_response=False)
|
|
|
|
self.assertEqual(r.wsgi_request.user, AnonymousUser())
|
|
|
|
self._is_cookie_deleted(
|
|
|
|
self.client, GenericLoginView.TOKEN_COOKIE_NAME)
|
|
|
|
|
|
|
|
def test_flow_ok(self):
|
|
|
|
"""
|
|
|
|
A team user is logged in as the kfet generic user.
|
|
|
|
"""
|
|
|
|
self.client.login(username='team', password='team')
|
|
|
|
next_url = '/k-fet/'
|
|
|
|
|
|
|
|
r = self.client.post(
|
|
|
|
'{}?next={}'.format(self.url, next_url), follow=True)
|
|
|
|
|
|
|
|
self.assertEqual(r.wsgi_request.user, self.generic_user)
|
|
|
|
self.assertEqual(r.wsgi_request.path, '/k-fet/')
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Temporary authentication
|
|
|
|
#
|
|
|
|
# Includes:
|
|
|
|
# - TemporaryAuthMiddleware
|
|
|
|
# - temporary_auth context processor
|
|
|
|
##
|
|
|
|
|
|
|
|
class TemporaryAuthTests(TestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
2017-10-26 11:38:11 +02:00
|
|
|
patcher_messages = mock.patch('cof.signals.messages')
|
2017-09-25 17:16:19 +02:00
|
|
|
patcher_messages.start()
|
|
|
|
self.addCleanup(patcher_messages.stop)
|
|
|
|
|
|
|
|
self.factory = RequestFactory()
|
|
|
|
|
|
|
|
user1_acc = Account(trigramme='000')
|
|
|
|
user1_acc.change_pwd('kfet_user1')
|
|
|
|
user1_acc.save({'username': 'user1'})
|
|
|
|
self.user1 = user1_acc.user
|
|
|
|
self.user1.set_password('user1')
|
|
|
|
self.user1.save()
|
|
|
|
|
|
|
|
user2_acc = Account(trigramme='100')
|
|
|
|
user2_acc.change_pwd('kfet_user2')
|
|
|
|
user2_acc.save({'username': 'user2'})
|
|
|
|
self.user2 = user2_acc.user
|
|
|
|
self.user2.set_password('user2')
|
|
|
|
self.user2.save()
|
|
|
|
|
|
|
|
self.perm = Permission.objects.get(
|
|
|
|
content_type__app_label='kfet', codename='is_team')
|
|
|
|
self.user2.user_permissions.add(self.perm)
|
|
|
|
|
|
|
|
def test_middleware_header(self):
|
|
|
|
"""
|
|
|
|
A user can be authenticated if ``HTTP_KFETPASSWORD`` header of a
|
|
|
|
request contains a valid kfet password.
|
|
|
|
"""
|
|
|
|
request = self.factory.get('/', HTTP_KFETPASSWORD='kfet_user2')
|
|
|
|
request.user = self.user1
|
|
|
|
|
|
|
|
TemporaryAuthMiddleware().process_request(request)
|
|
|
|
|
|
|
|
self.assertEqual(request.user, self.user2)
|
|
|
|
self.assertEqual(request.real_user, self.user1)
|
|
|
|
|
|
|
|
def test_middleware_post(self):
|
|
|
|
"""
|
|
|
|
A user can be authenticated if ``KFETPASSWORD`` of POST data contains
|
|
|
|
a valid kfet password.
|
|
|
|
"""
|
|
|
|
request = self.factory.post('/', {'KFETPASSWORD': 'kfet_user2'})
|
|
|
|
request.user = self.user1
|
|
|
|
|
|
|
|
TemporaryAuthMiddleware().process_request(request)
|
|
|
|
|
|
|
|
self.assertEqual(request.user, self.user2)
|
|
|
|
self.assertEqual(request.real_user, self.user1)
|
|
|
|
|
|
|
|
def test_middleware_invalid(self):
|
|
|
|
"""
|
|
|
|
The given password must be a password of an Account.
|
|
|
|
"""
|
|
|
|
request = self.factory.post('/', {'KFETPASSWORD': 'invalid'})
|
|
|
|
request.user = self.user1
|
|
|
|
|
|
|
|
TemporaryAuthMiddleware().process_request(request)
|
|
|
|
|
|
|
|
self.assertEqual(request.user, self.user1)
|
|
|
|
self.assertFalse(hasattr(request, 'real_user'))
|
|
|
|
|
|
|
|
def test_context_processor(self):
|
|
|
|
"""
|
|
|
|
Context variables give the real authenticated user and his permissions.
|
|
|
|
"""
|
|
|
|
self.client.login(username='user1', password='user1')
|
|
|
|
|
|
|
|
r = self.client.get('/k-fet/accounts/', HTTP_KFETPASSWORD='kfet_user2')
|
|
|
|
|
|
|
|
self.assertEqual(r.context['user'], self.user1)
|
|
|
|
self.assertNotIn('kfet.is_team', r.context['perms'])
|
|
|
|
|
|
|
|
def test_auth_not_persistent(self):
|
|
|
|
"""
|
|
|
|
The authentication is temporary, i.e. for one request.
|
|
|
|
"""
|
|
|
|
self.client.login(username='user1', password='user1')
|
|
|
|
|
|
|
|
r1 = self.client.get(
|
|
|
|
'/k-fet/accounts/', HTTP_KFETPASSWORD='kfet_user2')
|
|
|
|
self.assertEqual(r1.wsgi_request.user, self.user2)
|
|
|
|
|
|
|
|
r2 = self.client.get('/k-fet/accounts/')
|
|
|
|
self.assertEqual(r2.wsgi_request.user, self.user1)
|