diff --git a/apps/accounts/api_views.py b/apps/accounts/api_views.py index aa3f836d..82e914e0 100644 --- a/apps/accounts/api_views.py +++ b/apps/accounts/api_views.py @@ -113,24 +113,6 @@ class TokenRefreshView(TokenRefreshView): ) return Response({"error": "Unauthorized"}, status=status.HTTP_403_FORBIDDEN) - # Check if user account is still active before issuing new access token - raw_token = request.data.get("refresh") - if raw_token: - try: - from rest_framework_simplejwt.tokens import RefreshToken as RefreshTokenClass - token = RefreshTokenClass(raw_token) - user_id = token.payload.get("user_id") - if user_id: - user = User.objects.filter(id=user_id).first() - if user and not user.is_active: - logger.info(f"Token refresh blocked for disabled user: user_id={user_id}") - return Response( - {"error": "Account is disabled"}, - status=status.HTTP_403_FORBIDDEN - ) - except Exception: - pass # Let parent handle invalid tokens - return super().post(request, *args, **kwargs) diff --git a/apps/accounts/tests.py b/apps/accounts/tests.py index f3afdbc4..629207ba 100644 --- a/apps/accounts/tests.py +++ b/apps/accounts/tests.py @@ -1,7 +1,6 @@ -from django.test import TestCase, RequestFactory +from django.test import TestCase from django.contrib.auth import get_user_model from rest_framework.test import APIClient -from rest_framework import status User = get_user_model() @@ -70,214 +69,4 @@ class InitializeSuperuserTests(TestCase): self.assertEqual(response.status_code, 200) self.assertTrue(response.json()["superuser_exists"]) # Should NOT have created a new user - self.assertFalse(User.objects.filter(username="newadmin").exists()) - - -class TokenRefreshDisabledUserTests(TestCase): - """Tests for blocking token refresh on disabled users""" - - def setUp(self): - self.client = APIClient() - self.token_url = "/api/accounts/token/" - self.refresh_url = "/api/accounts/token/refresh/" - - def test_refresh_works_for_active_user(self): - """Active user should be able to refresh their token""" - user = User.objects.create_user(username="active", password="testpass123") - user.user_level = 1 - user.is_active = True - user.save() - - # Get tokens - login_response = self.client.post( - self.token_url, - {"username": "active", "password": "testpass123"}, - format="json", - ) - self.assertEqual(login_response.status_code, 200) - refresh_token = login_response.data["refresh"] - - # Refresh should succeed - refresh_response = self.client.post( - self.refresh_url, - {"refresh": refresh_token}, - format="json", - ) - self.assertEqual(refresh_response.status_code, 200) - self.assertIn("access", refresh_response.data) - - def test_refresh_blocked_for_disabled_user(self): - """Disabled user should not be able to refresh their token""" - user = User.objects.create_user(username="disabled", password="testpass123") - user.user_level = 1 - user.is_active = True - user.save() - - # Get tokens while user is active - login_response = self.client.post( - self.token_url, - {"username": "disabled", "password": "testpass123"}, - format="json", - ) - self.assertEqual(login_response.status_code, 200) - refresh_token = login_response.data["refresh"] - - # Disable the user - user.is_active = False - user.save() - - # Refresh should be blocked - refresh_response = self.client.post( - self.refresh_url, - {"refresh": refresh_token}, - format="json", - ) - self.assertEqual(refresh_response.status_code, 403) - - -class LastAdminProtectionTests(TestCase): - """Tests for preventing disabling the last active admin""" - - def setUp(self): - self.admin = User.objects.create_user(username="admin1", password="testpass123") - self.admin.user_level = 10 - self.admin.save() - - self.client = APIClient() - self.client.force_authenticate(user=self.admin) - - def test_cannot_disable_last_admin(self): - """Should reject disabling the only active admin""" - response = self.client.patch( - f"/api/accounts/users/{self.admin.id}/", - {"is_active": False}, - format="json", - ) - self.assertEqual(response.status_code, 400) - self.assertIn("is_active", response.data) - - # Verify admin is still active - self.admin.refresh_from_db() - self.assertTrue(self.admin.is_active) - - def test_can_disable_admin_when_another_exists(self): - """Should allow disabling an admin when another active admin exists""" - admin2 = User.objects.create_user(username="admin2", password="testpass123") - admin2.user_level = 10 - admin2.save() - - response = self.client.patch( - f"/api/accounts/users/{admin2.id}/", - {"is_active": False}, - format="json", - ) - self.assertEqual(response.status_code, 200) - - admin2.refresh_from_db() - self.assertFalse(admin2.is_active) - - def test_can_disable_non_admin_user(self): - """Should always allow disabling non-admin users""" - regular = User.objects.create_user(username="regular", password="testpass123") - regular.user_level = 1 - regular.save() - - response = self.client.patch( - f"/api/accounts/users/{regular.id}/", - {"is_active": False}, - format="json", - ) - self.assertEqual(response.status_code, 200) - - regular.refresh_from_db() - self.assertFalse(regular.is_active) - - def test_can_reenable_disabled_user(self): - """Should allow re-enabling a disabled user""" - regular = User.objects.create_user(username="disabled", password="testpass123") - regular.user_level = 1 - regular.is_active = False - regular.save() - - response = self.client.patch( - f"/api/accounts/users/{regular.id}/", - {"is_active": True}, - format="json", - ) - self.assertEqual(response.status_code, 200) - - regular.refresh_from_db() - self.assertTrue(regular.is_active) - - -class DisabledUserLoginTests(TestCase): - """Tests that disabled users cannot log in""" - - def setUp(self): - self.client = APIClient() - self.token_url = "/api/accounts/token/" - - def test_disabled_user_cannot_login(self): - """Disabled user should get rejected at login""" - user = User.objects.create_user(username="disabled", password="testpass123") - user.is_active = False - user.save() - - response = self.client.post( - self.token_url, - {"username": "disabled", "password": "testpass123"}, - format="json", - ) - self.assertEqual(response.status_code, 401) - - def test_active_user_can_login(self): - """Active user should be able to log in""" - user = User.objects.create_user(username="active", password="testpass123") - user.is_active = True - user.save() - - response = self.client.post( - self.token_url, - {"username": "active", "password": "testpass123"}, - format="json", - ) - self.assertEqual(response.status_code, 200) - self.assertIn("access", response.data) - - -class DisabledUserAccessTokenTests(TestCase): - """Tests that a disabled user's existing access token is rejected on authenticated endpoints""" - - def setUp(self): - self.client = APIClient() - self.token_url = "/api/accounts/token/" - self.users_url = "/api/accounts/users/" - - def test_existing_token_rejected_after_disable(self): - """Access token obtained while active should be rejected after user is disabled""" - user = User.objects.create_user(username="willdisable", password="testpass123") - user.user_level = 10 # Admin so they can access the users endpoint - user.is_active = True - user.save() - - # Get tokens while user is active - login_response = self.client.post( - self.token_url, - {"username": "willdisable", "password": "testpass123"}, - format="json", - ) - self.assertEqual(login_response.status_code, 200) - access_token = login_response.data["access"] - - # Verify token works while active - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {access_token}") - response = self.client.get(self.users_url) - self.assertEqual(response.status_code, 200) - - # Disable the user - user.is_active = False - user.save() - - # Same token should now be rejected - response = self.client.get(self.users_url) - self.assertIn(response.status_code, [401, 403]) \ No newline at end of file + self.assertFalse(User.objects.filter(username="newadmin").exists()) \ No newline at end of file diff --git a/frontend/src/components/forms/User.jsx b/frontend/src/components/forms/User.jsx index d76dd2ec..44ec17ac 100644 --- a/frontend/src/components/forms/User.jsx +++ b/frontend/src/components/forms/User.jsx @@ -42,7 +42,6 @@ const User = ({ user = null, isOpen, onClose }) => { xc_password: '', channel_profiles: [], hide_adult_content: false, - is_active: true, }, validate: (values) => ({ @@ -135,7 +134,6 @@ const User = ({ user = null, isOpen, onClose }) => { : ['0'], xc_password: customProps.xc_password || '', hide_adult_content: customProps.hide_adult_content || false, - is_active: user.is_active !== false, }); if (customProps.xc_password) { @@ -203,30 +201,6 @@ const User = ({ user = null, isOpen, onClose }) => { /> )} - {isAdmin && ( - - -
- -
-
-
- )} diff --git a/frontend/src/components/tables/UsersTable.jsx b/frontend/src/components/tables/UsersTable.jsx index e9cf0a4c..af4e3087 100644 --- a/frontend/src/components/tables/UsersTable.jsx +++ b/frontend/src/components/tables/UsersTable.jsx @@ -8,7 +8,6 @@ import useWarningsStore from '../../store/warnings'; import { SquarePlus, SquareMinus, SquarePen, Eye, EyeOff } from 'lucide-react'; import { ActionIcon, - Badge, Box, Text, Paper, @@ -148,20 +147,6 @@ const UsersTable = () => { {USER_LEVEL_LABELS[getValue()]} ), }, - { - header: 'Status', - accessorKey: 'is_active', - size: 90, - cell: ({ getValue }) => ( - - {getValue() !== false ? 'Active' : 'Disabled'} - - ), - }, { header: 'Username', accessorKey: 'username', @@ -331,7 +316,6 @@ const UsersTable = () => { name: renderHeaderCell, email: renderHeaderCell, user_level: renderHeaderCell, - is_active: renderHeaderCell, last_login: renderHeaderCell, date_joined: renderHeaderCell, custom_properties: renderHeaderCell,