2017-09-25 17:16:19 +02:00
|
|
|
from unittest import mock
|
2017-05-12 16:55:18 +02:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
|
2017-09-25 17:16:19 +02:00
|
|
|
from django.core import signing
|
|
|
|
from django.test import RequestFactory, TestCase
|
2019-03-19 10:18:56 +01:00
|
|
|
from django.urls import reverse
|
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
|
|
|
|
##
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
|
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 = [
|
2018-10-06 12:35:49 +02:00
|
|
|
Group.objects.create(name=prefix_name + name) for name in names
|
2017-05-12 16:55:18 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
# 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)
|
2018-10-06 12:35:49 +02:00
|
|
|
groups_field = form.fields["groups"]
|
2017-05-14 22:19:25 +02:00
|
|
|
self.assertQuerysetEqual(
|
2018-10-06 12:35:49 +02:00
|
|
|
groups_field.queryset, [repr(g) for g in self.kfet_groups], ordered=False
|
2017-05-14 22:19:25 +02:00
|
|
|
)
|
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
|
2018-10-06 12:35:49 +02:00
|
|
|
data = {"groups": [group.pk for group in self.kfet_groups]}
|
2017-05-12 16:55:18 +02:00
|
|
|
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
|
|
|
|
##
|
|
|
|
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
class AccountBackendTests(TestCase):
|
2017-09-25 17:16:19 +02:00
|
|
|
def setUp(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
self.request = RequestFactory().get("/")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
def test_valid(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
acc = Account(trigramme="000")
|
|
|
|
acc.change_pwd("valid")
|
|
|
|
acc.save({"username": "user"})
|
2017-09-25 17:16:19 +02:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
auth = AccountBackend().authenticate(self.request, kfet_password="valid")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
self.assertEqual(auth, acc.user)
|
|
|
|
|
|
|
|
def test_invalid(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
auth = AccountBackend().authenticate(self.request, kfet_password="invalid")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.assertIsNone(auth)
|
|
|
|
|
|
|
|
|
|
|
|
class GenericBackendTests(TestCase):
|
|
|
|
def setUp(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
self.request = RequestFactory().get("/")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
def test_valid(self):
|
|
|
|
token = GenericTeamToken.objects.create_token()
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
auth = GenericBackend().authenticate(self.request, kfet_token=token.token)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
self.assertEqual(auth, get_kfet_generic_user())
|
|
|
|
self.assertEqual(GenericTeamToken.objects.all().count(), 0)
|
|
|
|
|
|
|
|
def test_invalid(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
auth = GenericBackend().authenticate(self.request, kfet_token="invalid")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.assertIsNone(auth)
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Views
|
|
|
|
##
|
|
|
|
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
class GenericLoginViewTests(TestCase):
|
2017-09-25 17:16:19 +02:00
|
|
|
def setUp(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
patcher_messages = mock.patch("gestioncof.signals.messages")
|
2017-09-25 17:16:19 +02:00
|
|
|
patcher_messages.start()
|
|
|
|
self.addCleanup(patcher_messages.stop)
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
user_acc = Account(trigramme="000")
|
|
|
|
user_acc.save({"username": "user"})
|
2017-09-25 17:16:19 +02:00
|
|
|
self.user = user_acc.user
|
2018-10-06 12:35:49 +02:00
|
|
|
self.user.set_password("user")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.user.save()
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
team_acc = Account(trigramme="100")
|
|
|
|
team_acc.save({"username": "team"})
|
2017-09-25 17:16:19 +02:00
|
|
|
self.team = team_acc.user
|
2018-10-06 12:35:49 +02:00
|
|
|
self.team.set_password("team")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.team.save()
|
|
|
|
self.team.user_permissions.add(
|
2018-10-06 12:35:49 +02:00
|
|
|
Permission.objects.get(content_type__app_label="kfet", codename="is_team")
|
2017-09-25 17:16:19 +02:00
|
|
|
)
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
self.url = reverse("kfet.login.generic")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.generic_user = get_kfet_generic_user()
|
|
|
|
|
|
|
|
def test_url(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
self.assertEqual(self.url, "/k-fet/login/generic")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
def test_notoken_get(self):
|
|
|
|
"""
|
|
|
|
Send confirmation for user to emit POST request, instead of GET.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client.login(username="team", password="team")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
|
|
|
|
self.assertEqual(r.status_code, 200)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.assertTemplateUsed(r, "kfet/confirm_form.html")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
def test_notoken_post(self):
|
|
|
|
"""
|
|
|
|
POST request without token in COOKIES sets a token and redirects to
|
|
|
|
logout url.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client.login(username="team", password="team")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
r = self.client.post(self.url)
|
|
|
|
|
|
|
|
self.assertRedirects(
|
2018-10-06 12:35:49 +02:00
|
|
|
r, "/logout?next={}".format(self.url), fetch_redirect_response=False
|
2017-09-25 17:16:19 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
def test_notoken_not_team(self):
|
|
|
|
"""
|
|
|
|
Logged in user must be a team user to initiate login as generic user.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client.login(username="user", password="user")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
# With GET.
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
self.assertRedirects(
|
2018-10-06 12:35:49 +02:00
|
|
|
r, "/login?next={}".format(self.url), fetch_redirect_response=False
|
2017-09-25 17:16:19 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
# Also with POST.
|
|
|
|
r = self.client.post(self.url)
|
|
|
|
self.assertRedirects(
|
2018-10-06 12:35:49 +02:00
|
|
|
r, "/login?next={}".format(self.url), fetch_redirect_response=False
|
2017-09-25 17:16:19 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
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.
|
2018-10-06 12:35:49 +02:00
|
|
|
self.assertEqual(cookie.value, "")
|
|
|
|
self.assertEqual(cookie["expires"], "Thu, 01-Jan-1970 00:00:00 GMT")
|
|
|
|
self.assertEqual(cookie["max-age"], 0)
|
2017-09-25 17:16:19 +02:00
|
|
|
except AssertionError:
|
|
|
|
raise AssertionError("The cookie '%s' still exists." % key)
|
|
|
|
|
|
|
|
def test_withtoken_valid(self):
|
|
|
|
"""
|
|
|
|
The kfet generic user is logged in.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
token = GenericTeamToken.objects.create(token="valid")
|
2017-09-25 17:16:19 +02:00
|
|
|
self._set_signed_cookie(
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client, GenericLoginView.TOKEN_COOKIE_NAME, "valid"
|
|
|
|
)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
self.assertRedirects(r, reverse("kfet.kpsul"))
|
2017-09-25 17:16:19 +02:00
|
|
|
self.assertEqual(r.wsgi_request.user, self.generic_user)
|
2018-10-06 12:35:49 +02:00
|
|
|
self._is_cookie_deleted(self.client, GenericLoginView.TOKEN_COOKIE_NAME)
|
2017-09-25 17:16:19 +02:00
|
|
|
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(
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client, GenericLoginView.TOKEN_COOKIE_NAME, "invalid"
|
|
|
|
)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
r = self.client.get(self.url)
|
|
|
|
|
|
|
|
self.assertRedirects(r, self.url, fetch_redirect_response=False)
|
|
|
|
self.assertEqual(r.wsgi_request.user, AnonymousUser())
|
2018-10-06 12:35:49 +02:00
|
|
|
self._is_cookie_deleted(self.client, GenericLoginView.TOKEN_COOKIE_NAME)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
def test_flow_ok(self):
|
|
|
|
"""
|
|
|
|
A team user is logged in as the kfet generic user.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client.login(username="team", password="team")
|
|
|
|
next_url = "/k-fet/"
|
2017-09-25 17:16:19 +02:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
r = self.client.post("{}?next={}".format(self.url, next_url), follow=True)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
self.assertEqual(r.wsgi_request.user, self.generic_user)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.assertEqual(r.wsgi_request.path, "/k-fet/")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Temporary authentication
|
|
|
|
#
|
|
|
|
# Includes:
|
|
|
|
# - TemporaryAuthMiddleware
|
|
|
|
# - temporary_auth context processor
|
|
|
|
##
|
|
|
|
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
class TemporaryAuthTests(TestCase):
|
2017-09-25 17:16:19 +02:00
|
|
|
def setUp(self):
|
2018-10-06 12:35:49 +02:00
|
|
|
patcher_messages = mock.patch("gestioncof.signals.messages")
|
2017-09-25 17:16:19 +02:00
|
|
|
patcher_messages.start()
|
|
|
|
self.addCleanup(patcher_messages.stop)
|
|
|
|
|
|
|
|
self.factory = RequestFactory()
|
|
|
|
|
2017-11-19 18:41:39 +01:00
|
|
|
self.middleware = TemporaryAuthMiddleware(mock.Mock())
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
user1_acc = Account(trigramme="000")
|
|
|
|
user1_acc.change_pwd("kfet_user1")
|
|
|
|
user1_acc.save({"username": "user1"})
|
2017-09-25 17:16:19 +02:00
|
|
|
self.user1 = user1_acc.user
|
2018-10-06 12:35:49 +02:00
|
|
|
self.user1.set_password("user1")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.user1.save()
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
user2_acc = Account(trigramme="100")
|
|
|
|
user2_acc.change_pwd("kfet_user2")
|
|
|
|
user2_acc.save({"username": "user2"})
|
2017-09-25 17:16:19 +02:00
|
|
|
self.user2 = user2_acc.user
|
2018-10-06 12:35:49 +02:00
|
|
|
self.user2.set_password("user2")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.user2.save()
|
|
|
|
|
|
|
|
self.perm = Permission.objects.get(
|
2018-10-06 12:35:49 +02:00
|
|
|
content_type__app_label="kfet", codename="is_team"
|
|
|
|
)
|
2017-09-25 17:16:19 +02:00
|
|
|
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.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
request = self.factory.get("/", HTTP_KFETPASSWORD="kfet_user2")
|
2017-09-25 17:16:19 +02:00
|
|
|
request.user = self.user1
|
|
|
|
|
2017-11-19 18:41:39 +01:00
|
|
|
self.middleware(request)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
request = self.factory.post("/", {"KFETPASSWORD": "kfet_user2"})
|
2017-09-25 17:16:19 +02:00
|
|
|
request.user = self.user1
|
|
|
|
|
2017-11-19 18:41:39 +01:00
|
|
|
self.middleware(request)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
request = self.factory.post("/", {"KFETPASSWORD": "invalid"})
|
2017-09-25 17:16:19 +02:00
|
|
|
request.user = self.user1
|
|
|
|
|
2017-11-19 18:41:39 +01:00
|
|
|
self.middleware(request)
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
self.assertEqual(request.user, self.user1)
|
2018-10-06 12:35:49 +02:00
|
|
|
self.assertFalse(hasattr(request, "real_user"))
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
def test_context_processor(self):
|
|
|
|
"""
|
|
|
|
Context variables give the real authenticated user and his permissions.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client.login(username="user1", password="user1")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
r = self.client.get("/k-fet/accounts/", HTTP_KFETPASSWORD="kfet_user2")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
self.assertEqual(r.context["user"], self.user1)
|
|
|
|
self.assertNotIn("kfet.is_team", r.context["perms"])
|
2017-09-25 17:16:19 +02:00
|
|
|
|
|
|
|
def test_auth_not_persistent(self):
|
|
|
|
"""
|
|
|
|
The authentication is temporary, i.e. for one request.
|
|
|
|
"""
|
2018-10-06 12:35:49 +02:00
|
|
|
self.client.login(username="user1", password="user1")
|
2017-09-25 17:16:19 +02:00
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
r1 = self.client.get("/k-fet/accounts/", HTTP_KFETPASSWORD="kfet_user2")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.assertEqual(r1.wsgi_request.user, self.user2)
|
|
|
|
|
2018-10-06 12:35:49 +02:00
|
|
|
r2 = self.client.get("/k-fet/accounts/")
|
2017-09-25 17:16:19 +02:00
|
|
|
self.assertEqual(r2.wsgi_request.user, self.user1)
|