gestioCOF/kfet/auth/tests.py
2019-01-14 23:17:26 +01:00

453 lines
14 KiB
Python

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 . import KFET_GENERIC_TRIGRAMME, KFET_GENERIC_USERNAME
from .backends import AccountBackend, GenericBackend
from .fields import CorePermissionsField, GroupsField
from .forms import GroupForm, UserGroupForm
from .middleware import TemporaryAuthMiddleware
from .models import GenericTeamToken, Group, Permission
from .utils import get_kfet_generic_user
from .views import GenericLoginView
##
# 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):
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.
"""
self.kf_group.permissions.add(self.ot_perm)
selected = [self.kf_perm1, self.kf_perm2]
data = {"name": "A Group", "permissions": [perm.pk for perm 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.
"""
# 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)
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):
patcher_messages = mock.patch("gestioncof.signals.messages")
patcher_messages.start()
self.addCleanup(patcher_messages.stop)
self.factory = RequestFactory()
self.middleware = TemporaryAuthMiddleware(mock.Mock())
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
self.middleware(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
self.middleware(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
self.middleware(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)