From 9afe0f5f41612a4bc34ab22228eb59ecdfb5ffe5 Mon Sep 17 00:00:00 2001
From: Ethan Nguyen <etnguyen03@hotmail.com>
Date: Fri, 25 Mar 2022 13:37:21 -0500
Subject: [PATCH] feat(authentication): add ability to ban a user

---
 tjdests/apps/authentication/decorators.py      |  6 +++++-
 .../migrations/0014_user_is_banned.py          | 18 ++++++++++++++++++
 tjdests/apps/authentication/models.py          |  1 +
 tjdests/apps/authentication/tests.py           |  6 ++++++
 tjdests/apps/authentication/views.py           |  9 +++++++++
 tjdests/apps/destinations/views.py             |  4 ++--
 tjdests/test.py                                |  3 +++
 7 files changed, 44 insertions(+), 3 deletions(-)
 create mode 100644 tjdests/apps/authentication/migrations/0014_user_is_banned.py

diff --git a/tjdests/apps/authentication/decorators.py b/tjdests/apps/authentication/decorators.py
index 583c0af..0ffc050 100644
--- a/tjdests/apps/authentication/decorators.py
+++ b/tjdests/apps/authentication/decorators.py
@@ -7,7 +7,11 @@ from django.shortcuts import redirect
 def require_accept_tos(func):
     @functools.wraps(func)
     def wrapper(request: HttpRequest, *args, **kwargs):
-        if request.user.is_authenticated and not request.user.accepted_terms:
+        if (
+            request.user.is_authenticated
+            and not request.user.accepted_terms
+            and not request.user.is_banned
+        ):
             return redirect("authentication:tos")
 
         return func(request, *args, **kwargs)
diff --git a/tjdests/apps/authentication/migrations/0014_user_is_banned.py b/tjdests/apps/authentication/migrations/0014_user_is_banned.py
new file mode 100644
index 0000000..c64a320
--- /dev/null
+++ b/tjdests/apps/authentication/migrations/0014_user_is_banned.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.12 on 2022-03-25 18:33
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("authentication", "0013_alter_user_gpa"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="user",
+            name="is_banned",
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/tjdests/apps/authentication/models.py b/tjdests/apps/authentication/models.py
index 86e81c2..986d9f1 100644
--- a/tjdests/apps/authentication/models.py
+++ b/tjdests/apps/authentication/models.py
@@ -19,6 +19,7 @@ class User(AbstractUser):
 
     is_senior = models.BooleanField(default=False)
     is_student = models.BooleanField(default=False)
+    is_banned = models.BooleanField(default=False)
 
     nickname = models.CharField(max_length=30, blank=True)
     use_nickname = models.BooleanField(
diff --git a/tjdests/apps/authentication/tests.py b/tjdests/apps/authentication/tests.py
index 343731e..ff983bb 100644
--- a/tjdests/apps/authentication/tests.py
+++ b/tjdests/apps/authentication/tests.py
@@ -46,6 +46,12 @@ class AuthenticationTest(TJDestsTestCase):
             response = self.client.get(reverse("authentication:tos"))
             self.assertEqual(200, response.status_code)
 
+        # Test banned users
+        self.login(make_student=True, ban_user=True)
+        response = self.client.get(reverse("authentication:tos"))
+        self.assertEqual(302, response.status_code)
+        self.assertNotIn("_auth_user_id", self.client.session)
+
         # Make us a student and try again
         user = self.login(make_student=True)
         response = self.client.get(reverse("authentication:tos"))
diff --git a/tjdests/apps/authentication/views.py b/tjdests/apps/authentication/views.py
index 899c70c..cb2346b 100644
--- a/tjdests/apps/authentication/views.py
+++ b/tjdests/apps/authentication/views.py
@@ -29,6 +29,15 @@ def accept_tos_view(request: HttpRequest) -> HttpResponse:
         messages.error(request, "You must be a student to access this site.")
         return redirect(reverse("authentication:index"))
 
+    if request.user.is_banned:
+        logout(request)
+        messages.error(
+            request,
+            "You have been banned from this site. "
+            "Contact the site's administrator to appeal your ban.",
+        )
+        return redirect(reverse("authentication:index"))
+
     if request.user.accepted_terms:
         return redirect(reverse("authentication:index"))
 
diff --git a/tjdests/apps/destinations/views.py b/tjdests/apps/destinations/views.py
index 3669737..eb109d2 100644
--- a/tjdests/apps/destinations/views.py
+++ b/tjdests/apps/destinations/views.py
@@ -67,7 +67,7 @@ class StudentDestinationListView(
 
     def test_func(self):
         assert self.request.user.is_authenticated
-        return self.request.user.accepted_terms
+        return self.request.user.accepted_terms and not self.request.user.is_banned
 
     template_name = "destinations/student_list.html"
 
@@ -200,6 +200,6 @@ class CollegeDestinationListView(
 
     def test_func(self):
         assert self.request.user.is_authenticated
-        return self.request.user.accepted_terms
+        return self.request.user.accepted_terms and not self.request.user.is_banned
 
     template_name = "destinations/college_list.html"
diff --git a/tjdests/test.py b/tjdests/test.py
index 7cda356..5aae985 100644
--- a/tjdests/test.py
+++ b/tjdests/test.py
@@ -11,6 +11,7 @@ class TJDestsTestCase(TestCase):
         make_student: bool = False,
         make_senior: bool = False,
         make_superuser: bool = False,
+        ban_user: bool = False,
         publish_data: bool = False,
     ) -> User:
         """
@@ -22,6 +23,7 @@ class TJDestsTestCase(TestCase):
             make_student: Whether to make this user a student.
             make_senior: Whether to make this user a senior.
             make_superuser: Whether to make this user a superuser.
+            ban_user: Whether to ban the user.
             publish_data: Whether to publish this user's data.
         Return:
             The user.
@@ -33,6 +35,7 @@ class TJDestsTestCase(TestCase):
                 "is_staff": make_superuser,
                 "is_superuser": make_superuser,
                 "is_senior": make_senior,
+                "is_banned": ban_user,
                 "accepted_terms": accept_tos,
                 "publish_data": publish_data,
             },