diff --git a/bda/tests/test_views.py b/bda/tests/test_views.py index 75d01ec9..47cbd2bd 100644 --- a/bda/tests/test_views.py +++ b/bda/tests/test_views.py @@ -356,7 +356,9 @@ class TestReventeManageTest(TestCase): def test_can_get(self): client = Client() - client.force_login(self.user) + client.force_login( + self.user, backend="django.contrib.auth.backends.ModelBackend" + ) r = client.get(self.url) self.assertEqual(r.status_code, 200) diff --git a/bds/tests/test_views.py b/bds/tests/test_views.py index a40d3d85..ef6139f4 100644 --- a/bds/tests/test_views.py +++ b/bds/tests/test_views.py @@ -27,7 +27,9 @@ class TestHomeView(TestCase): def test_get(self, mock_messages): user = User.objects.create_user(username="random_user") give_bds_buro_permissions(user) - self.client.force_login(user) + self.client.force_login( + user, backend="django.contrib.auth.backends.ModelBackend" + ) resp = self.client.get(reverse("bds:home")) self.assertEquals(resp.status_code, 200) @@ -44,7 +46,7 @@ class TestRegistrationView(TestCase): self.assertRedirects(resp, login_url(next=url)) # Logged-in but unprivileged GET - client.force_login(user) + client.force_login(user, backend="django.contrib.auth.backends.ModelBackend") resp = client.get(url) self.assertEquals(resp.status_code, 403) @@ -64,7 +66,7 @@ class TestRegistrationView(TestCase): self.assertRedirects(resp, login_url(next=url)) # Logged-in but unprivileged GET - client.force_login(user) + client.force_login(user, backend="django.contrib.auth.backends.ModelBackend") resp = client.get(url) self.assertEquals(resp.status_code, 403) diff --git a/events/tests/test_views.py b/events/tests/test_views.py index ef3eda31..611f1871 100644 --- a/events/tests/test_views.py +++ b/events/tests/test_views.py @@ -54,7 +54,9 @@ class CSVExportAccessTest(MessagePatch, TestCase): def test_get(self): client = Client() - client.force_login(self.staff) + client.force_login( + self.staff, backend="django.contrib.auth.backends.ModelBackend" + ) r = client.get(self.url) self.assertEqual(r.status_code, 200) @@ -66,7 +68,7 @@ class CSVExportAccessTest(MessagePatch, TestCase): def test_unauthorised(self): client = Client() - client.force_login(self.u1) + client.force_login(self.u1, backend="django.contrib.auth.backends.ModelBackend") r = client.get(self.url) self.assertEqual(r.status_code, 403) @@ -86,7 +88,9 @@ class CSVExportContentTest(MessagePatch, CSVResponseMixin, TestCase): ) self.staff = make_staff_user("staff") self.client = Client() - self.client.force_login(self.staff) + self.client.force_login( + self.staff, backend="django.contrib.auth.backends.ModelBackend" + ) def test_simple_event(self): self.event.subscribers.set([self.u1, self.u2]) diff --git a/gestioasso/settings/cof_prod.py b/gestioasso/settings/cof_prod.py index 28133ebc..b8b1c0ff 100644 --- a/gestioasso/settings/cof_prod.py +++ b/gestioasso/settings/cof_prod.py @@ -111,11 +111,17 @@ CORS_ORIGIN_WHITELIST = ("bda.ens.fr", "www.bda.ens.fr" "cof.ens.fr", "www.cof.e # Auth-related stuff # --- -AUTHENTICATION_BACKENDS += [ - "gestioncof.shared.COFCASBackend", - "kfet.auth.backends.GenericBackend", -] - +AUTHENTICATION_BACKENDS = ( + [ + # Must be in first + "kfet.auth.backends.BlockFrozenAccountBackend" + ] + + AUTHENTICATION_BACKENDS + + [ + "gestioncof.shared.COFCASBackend", + "kfet.auth.backends.GenericBackend", + ] +) LOGIN_URL = "cof-login" LOGIN_REDIRECT_URL = "home" diff --git a/gestioncof/tests/test_views.py b/gestioncof/tests/test_views.py index dc9b8df0..ecbb20f6 100644 --- a/gestioncof/tests/test_views.py +++ b/gestioncof/tests/test_views.py @@ -641,7 +641,7 @@ class ClubListViewTests(ViewTestCaseMixin, TestCase): def test_as_staff(self): u = self.users["staff"] c = Client() - c.force_login(u) + c.force_login(u, backend="django.contrib.auth.backends.ModelBackend") r = c.get(self.url) @@ -686,7 +686,7 @@ class ClubMembersViewTests(ViewTestCaseMixin, TestCase): self.c.respos.add(u) c = Client() - c.force_login(u) + c.force_login(u, backend="django.contrib.auth.backends.ModelBackend") r = c.get(self.url) self.assertEqual(r.status_code, 200) diff --git a/kfet/auth/backends.py b/kfet/auth/backends.py index 55e18458..0f7789a1 100644 --- a/kfet/auth/backends.py +++ b/kfet/auth/backends.py @@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model +from django.core.exceptions import PermissionDenied from kfet.models import Account, GenericTeamToken @@ -37,3 +38,36 @@ class GenericBackend(BaseKFetBackend): team_token.delete() return get_kfet_generic_user() + + +class BlockFrozenAccountBackend: + def authenticate(self, request, **kwargs): + return None + + def get_user(self, user_id): + return None + + def has_perm(self, user_obj, perm, obj=None): + app_label, _ = perm.split(".") + if app_label == "kfet": + if ( + hasattr(user_obj, "profile") + and hasattr(user_obj.profile, "account_kfet") + and user_obj.profile.account_kfet.is_frozen + ): + raise PermissionDenied + + # Dans le cas général, on se réfère aux autres backends + return False + + def has_module_perms(self, user_obj, app_label): + if app_label == "kfet": + if ( + hasattr(user_obj, "profile") + and hasattr(user_obj.profile, "account_kfet") + and user_obj.profile.account_kfet.is_frozen + ): + raise PermissionDenied + + # Dans le cas général, on se réfère aux autres backends + return False diff --git a/kfet/forms.py b/kfet/forms.py index d728afb1..e0d32102 100644 --- a/kfet/forms.py +++ b/kfet/forms.py @@ -61,7 +61,7 @@ class AccountForm(forms.ModelForm): class Meta: model = Account - fields = ["trigramme", "promo", "nickname", "is_frozen"] + fields = ["trigramme", "promo", "nickname"] widgets = {"trigramme": forms.TextInput(attrs={"autocomplete": "off"})} @@ -119,6 +119,12 @@ class AccountPwdForm(forms.Form): return self.account +class AccountFrozenForm(forms.ModelForm): + class Meta: + model = Account + fields = ["is_frozen"] + + class CofForm(forms.ModelForm): def clean_is_cof(self): instance = getattr(self, "instance", None) diff --git a/kfet/migrations/0076_unfreeze_accounts.py b/kfet/migrations/0076_unfreeze_accounts.py new file mode 100644 index 00000000..23901d99 --- /dev/null +++ b/kfet/migrations/0076_unfreeze_accounts.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.17 on 2021-02-23 22:51 + +from django.db import migrations + + +def unfreeze_accounts(apps, schema_editor): + Account = apps.get_model("kfet", "Account") + Account.objects.all().update(is_frozen=False) + + +class Migration(migrations.Migration): + + dependencies = [ + ("kfet", "0075_remove_accountnegative_balance_offset"), + ] + + operations = [migrations.RunPython(unfreeze_accounts, migrations.RunPython.noop)] diff --git a/kfet/migrations/0077_delete_frozen_permission.py b/kfet/migrations/0077_delete_frozen_permission.py new file mode 100644 index 00000000..8ac297fa --- /dev/null +++ b/kfet/migrations/0077_delete_frozen_permission.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.17 on 2021-02-23 23:22 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("kfet", "0076_unfreeze_accounts"), + ] + + operations = [ + migrations.AlterModelOptions( + name="operation", + options={ + "permissions": ( + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ) + }, + ), + ] diff --git a/kfet/models.py b/kfet/models.py index 7156ae52..628e5de6 100644 --- a/kfet/models.py +++ b/kfet/models.py @@ -180,9 +180,6 @@ class Account(models.Model): return set(), False if self.need_comment: perms.add("kfet.perform_commented_operations") - # Checking is frozen account - if self.is_frozen: - perms.add("kfet.override_frozen_protection") new_balance = self.balance + amount if new_balance < 0 and amount < 0: # Retrieving overdraft amount limit @@ -726,7 +723,6 @@ class Operation(models.Model): permissions = ( ("perform_deposit", "Effectuer une charge"), ("perform_negative_operations", "Enregistrer des commandes en négatif"), - ("override_frozen_protection", "Forcer le gel d'un compte"), ("cancel_old_operations", "Annuler des commandes non récentes"), ( "perform_commented_operations", diff --git a/kfet/open/tests.py b/kfet/open/tests.py index 0d527644..455f2cef 100644 --- a/kfet/open/tests.py +++ b/kfet/open/tests.py @@ -211,7 +211,7 @@ class OpenKfetConsumerTest(ChannelTestCase): ) t.user_permissions.add(is_team) c = WSClient() - c.force_login(t) + c.force_login(t, backend="django.contrib.auth.backends.ModelBackend") # connect c.send_and_consume( @@ -251,7 +251,9 @@ class OpenKfetScenarioTest(ChannelTestCase): self.r_c.login(username="root", password="root") # its client (for websockets) self.r_c_ws = WSClient() - self.r_c_ws.force_login(self.r) + self.r_c_ws.force_login( + self.r, backend="django.contrib.auth.backends.ModelBackend" + ) self.kfet_open = OpenKfet( cache_prefix="test_kfetopen_%s" % random.randrange(2 ** 20) diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js index 1ff3c583..2030304f 100644 --- a/kfet/static/kfet/js/kfet.js +++ b/kfet/static/kfet/js/kfet.js @@ -150,6 +150,12 @@ function getErrorsHtml(data) { content += '
  • Opération invalide sur le compte ' + data['errors']['account'] + '
  • '; content += ''; } + if ('frozen' in data['errors']) { + content += 'Général'; + content += ''; + } return content; } @@ -206,6 +212,17 @@ function requestAuth(data, callback, focus_next = null) { }); } +function displayErrors(html) { + $.alert({ + title: 'Erreurs', + content: html, + backgroundDismiss: true, + animation: 'top', + closeAnimation: 'bottom', + keyboardEnabled: true, + }); +} + /** * Setup jquery-confirm diff --git a/kfet/templates/kfet/account_update.html b/kfet/templates/kfet/account_update.html index dcb55555..2bab6c1d 100644 --- a/kfet/templates/kfet/account_update.html +++ b/kfet/templates/kfet/account_update.html @@ -32,6 +32,7 @@ Modification de mes informations {% csrf_token %} {% include 'kfet/form_snippet.html' with form=user_info_form %} {% include 'kfet/form_snippet.html' with form=account_form %} + {% include 'kfet/form_snippet.html' with form=frozen_form %} {% include 'kfet/form_snippet.html' with form=group_form %} {% include 'kfet/form_snippet.html' with form=pwd_form %} {% include 'kfet/form_snippet.html' with form=negative_form %} diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index 03ade3c6..ece98578 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -340,21 +340,6 @@ $(document).ready(function() { $('#id_comment').val(''); } - // ----- - // Errors ajax - // ----- - - function displayErrors(html) { - $.alert({ - title: 'Erreurs', - content: html, - backgroundDismiss: true, - animation: 'top', - closeAnimation: 'bottom', - keyboardEnabled: true, - }); - } - // ----- // Perform operations // ----- diff --git a/kfet/templates/kfet/transfers_create.html b/kfet/templates/kfet/transfers_create.html index e4fae405..a4a1a450 100644 --- a/kfet/templates/kfet/transfers_create.html +++ b/kfet/templates/kfet/transfers_create.html @@ -121,6 +121,9 @@ $(document).ready(function () { case 403: requestAuth(data, performTransfers); break; + case 400: + displayErrors(getErrorsHtml(data)); + break; } }); } diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index bc50b023..c4d31ae2 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -1926,8 +1926,7 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ["[kfet] Enregistrer des commandes avec commentaires"], ) - def test_group_on_acc_frozen(self): - user_add_perms(self.users["team"], ["kfet.override_frozen_protection"]) + def test_error_on_acc_frozen(self): self.account.is_frozen = True self.account.save() @@ -1944,30 +1943,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ) resp = self.client.post(self.url, data) - self._assertResponseOk(resp) - - def test_invalid_group_on_acc_frozen_requires_perm(self): - self.account.is_frozen = True - self.account.save() - - data = dict( - self.base_post_data, - **{ - "comment": "A comment to explain it", - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - } - ) - resp = self.client.post(self.url, data) - - self.assertEqual(resp.status_code, 403) + self.assertEqual(resp.status_code, 400) json_data = json.loads(resp.content.decode("utf-8")) - self.assertEqual( - json_data["errors"]["missing_perms"], ["[kfet] Forcer le gel d'un compte"] - ) + self.assertEqual(json_data["errors"]["frozen"], [self.account.trigramme]) def test_invalid_group_checkout(self): self.checkout.valid_from -= timedelta(days=300) diff --git a/kfet/tests/testcases.py b/kfet/tests/testcases.py index 16ccb186..a7962f33 100644 --- a/kfet/tests/testcases.py +++ b/kfet/tests/testcases.py @@ -253,7 +253,10 @@ class ViewTestCaseMixin(TestCaseMixin): self.register_user(label, user) if self.auth_user: - self.client.force_login(self.users[self.auth_user]) + self.client.force_login( + self.users[self.auth_user], + backend="django.contrib.auth.backends.ModelBackend", + ) def tearDown(self): del self.users_base diff --git a/kfet/views.py b/kfet/views.py index 0423be07..83bf380a 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -38,6 +38,7 @@ from kfet.config import kfet_config from kfet.decorators import teamkfet_required from kfet.forms import ( AccountForm, + AccountFrozenForm, AccountNegativeForm, AccountNoTriForm, AccountPwdForm, @@ -349,10 +350,11 @@ def account_update(request, trigramme): return HttpResponseForbidden user_info_form = UserInfoForm(instance=account.user) - - group_form = UserGroupForm(instance=account.user) account_form = AccountForm(instance=account) + group_form = UserGroupForm(instance=account.user) + frozen_form = AccountFrozenForm(request.POST, instance=account) pwd_form = AccountPwdForm() + if hasattr(account, "negative"): negative_form = AccountNegativeForm(instance=account.negative) else: @@ -362,6 +364,7 @@ def account_update(request, trigramme): self_update = request.user == account.user account_form = AccountForm(request.POST, instance=account) group_form = UserGroupForm(request.POST, instance=account.user) + frozen_form = AccountFrozenForm(request.POST, instance=account) pwd_form = AccountPwdForm(request.POST, account=account) forms = [] @@ -374,6 +377,7 @@ def account_update(request, trigramme): if request.user.has_perm("kfet.manage_perms"): forms.append(group_form) + forms.append(frozen_form) elif group_form.has_changed(): warnings.append("statut d'équipe") @@ -431,6 +435,7 @@ def account_update(request, trigramme): "user_info_form": user_info_form, "account": account, "account_form": account_form, + "frozen_form": frozen_form, "group_form": group_form, "negative_form": negative_form, "pwd_form": pwd_form, @@ -1051,6 +1056,9 @@ def kpsul_perform_operations(request): ) need_comment = operationgroup.on_acc.need_comment + if operationgroup.on_acc.is_frozen: + data["errors"]["frozen"] = [operationgroup.on_acc.trigramme] + # Filling data of each operations # + operationgroup + calculating other stuffs for operation in operations: @@ -1672,7 +1680,11 @@ def perform_transfers(request): negative_accounts = [] # Checking if ok on all accounts + frozen = set() for account in to_accounts_balances: + if account.is_frozen: + frozen.add(account.trigramme) + (perms, stop) = account.perms_to_perform_operation( amount=to_accounts_balances[account] ) @@ -1681,6 +1693,10 @@ def perform_transfers(request): if stop: negative_accounts.append(account.trigramme) + if len(frozen): + data["errors"]["frozen"] = list(frozen) + return JsonResponse(data, status=400) + if stop_all or not request.user.has_perms(required_perms): missing_perms = get_missing_perms(required_perms, request.user) if missing_perms: diff --git a/petitscours/tests/test_views.py b/petitscours/tests/test_views.py index 6ca97086..9367c258 100644 --- a/petitscours/tests/test_views.py +++ b/petitscours/tests/test_views.py @@ -77,7 +77,9 @@ class PetitCoursInscriptionViewTestCase(ViewTestCaseMixin, TestCase): self.subject2 = create_petitcours_subject(name="Matière 2") def test_get_forbidden_user_not_cof(self): - self.client.force_login(self.users["user"]) + self.client.force_login( + self.users["user"], backend="django.contrib.auth.backends.ModelBackend" + ) resp = self.client.get(self.url) self.assertRedirects(resp, reverse("cof-denied"))