kpsul/kfet/auth/tests.py
Aurélien Delobelle a7dbc64e2b Fix SnippetsCmsForm
+ Prevent querying the database from tests too soon.

=> tests pass.
2017-10-25 20:57:17 +02:00

640 lines
19 KiB
Python

# -*- coding: utf-8 -*-
from unittest import mock
from django.contrib.auth.models import (
AnonymousUser, Group as DjangoGroup, User,
)
from django.contrib.contenttypes.models import ContentType
from django.core import signing
from django.core.urlresolvers import reverse
from django.test import RequestFactory, TestCase
from kfet.models import Account
from kfet.tests.testcases import ViewTestCaseMixin
from kfet.tests.utils import create_team, get_perms
from . import KFET_GENERIC_TRIGRAMME, KFET_GENERIC_USERNAME
from .backends import AccountBackend, GenericBackend
from .fields import GroupsField, CorePermissionsField
from .middleware import TemporaryAuthMiddleware
from .models import GenericTeamToken, Group, Permission
from .utils import get_kfet_generic_user
##
# Forms and form fields
##
class CorePermissionsFieldTests(TestCase):
def setUp(self):
kf_ct = ContentType.objects.get_for_model(GenericTeamToken)
self.kf_perm1 = Permission.objects.create(
content_type=kf_ct, codename='code1')
self.kf_perm2 = Permission.objects.create(
content_type=kf_ct, codename='code2')
self.kf_perm3 = Permission.objects.create(
content_type=kf_ct, codename='code3')
ot_ct = ContentType.objects.get_for_model(Permission)
self.ot_perm = Permission.objects.create(
content_type=ot_ct, codename='code')
def test_choices(self):
"""
Only K-Fêt permissions are selectable.
"""
field = CorePermissionsField()
for p in [self.kf_perm1, self.kf_perm2, self.kf_perm3]:
self.assertIn(p, field.queryset)
self.assertNotIn(self.ot_perm, field.queryset)
class GroupsFieldTests(TestCase):
def setUp(self):
self.kf_group1 = Group.objects.create(name='KF Group1')
self.kf_group2 = Group.objects.create(name='KF Group2')
self.kf_group3 = Group.objects.create(name='KF Group3')
self.ot_group = DjangoGroup.objects.create(name='OT Group')
def test_choices(self):
"""
Only K-Fêt groups are selectable.
"""
field = GroupsField()
self.assertQuerysetEqual(
field.queryset,
map(repr, [self.kf_group1, self.kf_group2, self.kf_group3]),
ordered=False,
)
class GroupFormTests(TestCase):
def setUp(self):
self.user = User.objects.create(username='user')
self.kf_group = Group.objects.create(name='A Group')
self.kf_perm1 = Permission.kfetcore.get(codename='is_team')
self.kf_perm2 = Permission.kfetcore.get(codename='add_checkout')
ot_ct = ContentType.objects.get_for_model(Permission)
self.ot_perm = Permission.objects.create(
content_type=ot_ct, codename='cool')
def test_creation(self):
from .forms import GroupForm
data = {
'name': 'Another Group',
'permissions': [self.kf_perm1.pk],
}
form = GroupForm(data)
instance = form.save()
self.assertEqual(instance.name, 'Another Group')
self.assertQuerysetEqual(
instance.permissions.all(), map(repr, [self.kf_perm1]))
def test_keep_others(self):
"""
Non-kfet permissions of Group are kept when the form is submitted.
Regression test for #168.
"""
from .forms import GroupForm
self.kf_group.permissions.add(self.ot_perm)
selected = [self.kf_perm1, self.kf_perm2]
data = {
'name': 'A Group',
'permissions': [str(p.pk) for p in selected],
}
form = GroupForm(data, instance=self.kf_group)
form.save()
self.assertQuerysetEqual(
self.kf_group.permissions.all(),
map(repr, [self.ot_perm] + selected),
ordered=False,
)
class UserGroupFormTests(TestCase):
def setUp(self):
# create user
self.user = User.objects.create(username="foo", password="foo")
# create some K-Fêt groups
self.kf_group1 = Group.objects.create(name='KF Group1')
self.kf_group2 = Group.objects.create(name='KF Group2')
self.kf_group3 = Group.objects.create(name='KF Group3')
# create a non-K-Fêt group
self.ot_group = DjangoGroup.objects.create(name="OT Group")
def test_keep_others(self):
"""
User stays in its non-K-Fêt groups.
Regression test for #161.
"""
from .forms import UserGroupForm
# add user to a non-K-Fêt group
self.user.groups.add(self.ot_group)
# add user to some K-Fêt groups through UserGroupForm
selected = [self.kf_group1, self.kf_group2]
data = {'groups': [str(g.pk) for g in selected]}
form = UserGroupForm(data, instance=self.user)
form.save()
transform = lambda g: g.pk
self.assertQuerysetEqual(
self.user.groups.all(),
map(transform, [self.ot_group] + selected),
ordered=False, transform=transform,
)
##
# Models
##
class PermissionTests(TestCase):
def test_manager_kfet(self):
"""
'kfet' manager only returns K-Fêt permissions.
"""
kf_ct = ContentType.objects.get_for_model(GenericTeamToken)
kf_perm1 = Permission.objects.create(
content_type=kf_ct, codename='code1')
kf_perm2 = Permission.objects.create(
content_type=kf_ct, codename='code2')
kf_perm3 = Permission.objects.create(
content_type=kf_ct, codename='code3')
self.assertEqual(Permission.kfetcore.get(codename='code1'), kf_perm1)
self.assertEqual(Permission.kfetcore.get(codename='code2'), kf_perm2)
self.assertEqual(Permission.kfetcore.get(codename='code3'), kf_perm3)
ot_ct = ContentType.objects.get_for_model(Permission)
Permission.objects.create(content_type=ot_ct, codename='code')
with self.assertRaises(Permission.DoesNotExist):
Permission.kfetcore.get(codename='code')
##
# K-Fêt generic user object
##
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)
##
# 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):
patcher_messages = mock.patch('gestioncof.signals.messages')
patcher_messages.start()
self.addCleanup(patcher_messages.stop)
# Prevent querying the database too soon.
from .views import GenericLoginView
self.view_cls = GenericLoginView
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, self.view_cls.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, self.view_cls.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, self.view_cls.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, self.view_cls.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/')
class GroupListViewTests(ViewTestCaseMixin, TestCase):
url_name = 'kfet.group'
url_expected = '/k-fet/groupes/'
auth_user = 'team1'
auth_forbidden = [None, 'user', 'team']
def get_users_extra(self):
return {
'team1': create_team('team1', '101', perms=[
'kfetauth.view_group']),
}
def setUp(self):
super().setUp()
self.group1 = Group.objects.create(name='K-Fêt - Group1')
self.group2 = Group.objects.create(name='K-Fêt - Group2')
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context['groups'],
map(repr, [self.group1, self.group2]),
ordered=False,
)
class GroupCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = 'kfet.group.create'
url_expected = '/k-fet/groupes/nouveau/'
http_methods = ['GET', 'POST']
auth_user = 'team1'
auth_forbidden = [None, 'user', 'team']
def get_users_extra(self):
return {
'team1': create_team('team1', '101', perms=['kfetauth.add_group']),
}
@property
def post_data(self):
return {
'name': 'The Group',
'permissions': [
str(self.perms['kfet.is_team'].pk),
str(self.perms['kfetauth.add_group'].pk),
],
}
def setUp(self):
super().setUp()
self.perms = get_perms(
'kfet.is_team',
'kfetauth.add_group',
)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
r = self.client.post(self.url, self.post_data)
self.assertRedirects(
r, reverse('kfet.group'),
fetch_redirect_response=False, # Requires `kfetauth.view_group`.
)
group = Group.objects.get(name='The Group')
self.assertQuerysetEqual(
group.permissions.all(),
map(repr, [
self.perms['kfet.is_team'],
self.perms['kfetauth.add_group'],
]),
ordered=False,
)
class GroupUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = 'kfet.group.update'
http_methods = ['GET', 'POST']
auth_user = 'team1'
auth_forbidden = [None, 'user', 'team']
@property
def url_kwargs(self):
return {'pk': self.group.pk}
@property
def url_expected(self):
return '/k-fet/groupes/{}/edition/'.format(self.group.pk)
def get_users_extra(self):
return {
'team1': create_team('team1', '101', perms=[
'kfetauth.change_group']),
}
@property
def post_data(self):
return {
'name': 'The Group',
'permissions': [
str(self.perms['kfet.is_team'].pk),
str(self.perms['kfetauth.change_group'].pk),
],
}
def setUp(self):
super().setUp()
self.perms = get_perms(
'kfet.is_team',
'kfetauth.change_group',
)
self.group = Group.objects.create(name='K-Fêt - Group')
self.group.permissions = self.perms.values()
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
r = self.client.post(self.url, self.post_data)
self.assertRedirects(
r, reverse('kfet.group'),
fetch_redirect_response=False, # Requires `kfetauth.view_group`.
)
self.group.refresh_from_db()
self.assertEqual(self.group.name, 'The Group')
self.assertQuerysetEqual(
self.group.permissions.all(),
map(repr, [
self.perms['kfet.is_team'],
self.perms['kfetauth.change_group'],
]),
ordered=False,
)
##
# Temporary authentication
#
# Includes:
# - TemporaryAuthMiddleware
# - temporary_auth context processor
##
class TemporaryAuthTests(TestCase):
def setUp(self):
patcher_messages = mock.patch('gestioncof.signals.messages')
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)