diff --git a/mcserver/views.py b/mcserver/views.py index cacadcd..4a224c2 100644 --- a/mcserver/views.py +++ b/mcserver/views.py @@ -2001,7 +2001,19 @@ def get_queryset(self): return Subject.objects.filter(user=user).prefetch_related('subjecttags_set') def list(self, request): + """ + Default to the user's own subjects. + + admin/backend can pass ?all_subjects=true to list across all users; + no-op for other users. + """ queryset = self.get_queryset() + all_subjects = request.query_params.get('all_subjects', 'false') == 'true' + if not all_subjects: + queryset = queryset.filter(user=request.user) + # snapshot before search/trashed/paging narrow queryset below (see total) + base_queryset = queryset + # Get quantity from post request. If it does exist, use it. If not, set -1 as default (e.g., return all) # print(request.query_params) is_simple = request.query_params.get('simple', 'false') == 'true' @@ -2036,7 +2048,7 @@ def list(self, request): queryset = queryset[:quantity] serializer = (SimpleSubjectSerializer if is_simple else SubjectSerializer)(queryset, many=True) - return Response({'subjects': serializer.data, 'total': self.get_queryset().count()}) + return Response({'subjects': serializer.data, 'total': base_queryset.count()}) def get_serializer_class(self): if self.action in ['create', 'update', 'partial_update']: diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 3ebfc63..7bf1a48 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -916,21 +916,58 @@ def _setup_subject(self): self.subject = Subject.objects.create(name='test_subject', user=self.owner) self.detail_url = f'/subjects/{self.subject.pk}/' + def _returned_ids(self, resp): + # Ids come back as UUIDs; compare as strings to avoid type mismatches. + return {str(s['id']) for s in resp.data['subjects']} + def test_get_list(self): - # Test GET /subjects/ (list) + # Test GET /subjects/ (list) - by default every user, including admin + # and backend, only sees their own subjects. self._setup_subject() + other_subject = Subject.objects.create(name='other_subject', user=self.other_user) + + # Exact subjects each role should see by default (their own only). + expected_ids = { + 'owner': {str(self.subject.pk)}, + 'admin': set(), + 'backend': set(), + 'other': {str(other_subject.pk)}, + } for role in self.users: with self.subTest(role=role): self.client.force_authenticate(user=self.users[role]) resp = self.client.get(self.list_url) - if role in ['owner', 'admin', 'backend', 'other']: + if role in expected_ids: self.assertEqual(resp.status_code, 200) + self.assertEqual(self._returned_ids(resp), expected_ids[role]) + self.assertEqual(resp.data['total'], len(expected_ids[role])) + else: + self.assertEqual(resp.status_code, 403) - if role in ['owner', 'admin', 'backend']: - self.assertEqual(len(resp.data['subjects']), 1) - else: - self.assertEqual(len(resp.data['subjects']), 0) + def test_get_list_all_subjects_flag(self): + # Test GET /subjects/?all_subjects=true - admin/backend can list every + # user's subjects; the flag is ignored for everyone else. + self._setup_subject() + other_subject = Subject.objects.create(name='other_subject', user=self.other_user) + all_ids = {str(self.subject.pk), str(other_subject.pk)} + + # Elevated roles see every subject; everyone else still only their own. + expected_ids = { + 'admin': all_ids, + 'backend': all_ids, + 'owner': {str(self.subject.pk)}, + 'other': {str(other_subject.pk)}, + } + for role in self.users: + with self.subTest(role=role): + self.client.force_authenticate(user=self.users[role]) + resp = self.client.get(self.list_url, {'all_subjects': 'true'}) + + if role in expected_ids: + self.assertEqual(resp.status_code, 200) + self.assertEqual(self._returned_ids(resp), expected_ids[role]) + self.assertEqual(resp.data['total'], len(expected_ids[role])) else: self.assertEqual(resp.status_code, 403)