From 9665dbc05e37cda6a7443acb824375befb61c8cc Mon Sep 17 00:00:00 2001 From: ManishShah120 Date: Sat, 22 May 2021 17:18:10 +0530 Subject: [PATCH 1/5] [feature] Implemented view permissions by extending DjangoModelPermissions class #249 Closes #249 --- openwisp_users/api/permissions.py | 33 ++++++++++++++++++- .../testapp/tests/test_permission_classes.py | 30 +++++++++++++++++ tests/testapp/views.py | 6 +++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/openwisp_users/api/permissions.py b/openwisp_users/api/permissions.py index 8979b642..8c0c819e 100644 --- a/openwisp_users/api/permissions.py +++ b/openwisp_users/api/permissions.py @@ -1,5 +1,7 @@ +import copy + from django.utils.translation import gettext_lazy as _ -from rest_framework.permissions import BasePermission +from rest_framework.permissions import BasePermission, DjangoModelPermissions from swapper import load_model Organization = load_model('openwisp_users', 'Organization') @@ -66,3 +68,32 @@ class IsOrganizationOwner(BaseOrganizationPermission): def validate_membership(self, user, org): return org and (user.is_superuser or user.is_owner(org)) + + +class CustomDjangoModelPermissions(DjangoModelPermissions): + def __init__(self): + self.perms_map = copy.deepcopy(self.perms_map) + self.perms_map['GET'] = ['%(app_label)s.view_%(model_name)s'] + + def has_permission(self, request, view): + # Workaround to ensure DjangoModelPermissions are not applied + # to the root view when using DefaultRouter. + if getattr(view, '_ignore_model_permissions', False): + return True + + if not request.user or ( + not request.user.is_authenticated and self.authenticated_users_only + ): + return False + + queryset = self._queryset(view) + perms = self.get_required_permissions(request.method, queryset.model) + change_perm = self.get_required_permissions('PUT', queryset.model) + + if request.method == 'GET': + if request.user.has_perms(perms) or request.user.has_perms(change_perm): + return True + else: + return False + + return request.user.has_perms(perms) diff --git a/tests/testapp/tests/test_permission_classes.py b/tests/testapp/tests/test_permission_classes.py index 89b7b354..2739fecf 100644 --- a/tests/testapp/tests/test_permission_classes.py +++ b/tests/testapp/tests/test_permission_classes.py @@ -1,3 +1,5 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission from django.test import TestCase from django.urls import reverse @@ -5,6 +7,8 @@ from .mixins import TestMultitenancyMixin +User = get_user_model() + class TestPermissionClasses(TestMultitenancyMixin, TestCase): def setUp(self): @@ -117,3 +121,29 @@ def test_organization_field_with_errored_parent(self): with self.assertRaises(AttributeError) as error: self.client.get(reverse('test_error_field_view'), **auth) self.assertIn('Organization not found', str(error.exception)) + + def test_custom_django_model_permission_with_view_permission(self): + user = User.objects.create_user( + username='operator', password='tester', email='operator@test.com' + ) + user_permissions = Permission.objects.filter(codename='view_template') + user.user_permissions.add(*user_permissions) + user.organizations_dict # force caching + self.client.force_login(user) + token = self._obtain_auth_token() + auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + response = self.client.get(reverse('test_template_list'), **auth) + self.assertEqual(response.status_code, 200) + + def test_custom_django_model_permission_with_change_permission(self): + user = User.objects.create_user( + username='operator', password='tester', email='operator@test.com' + ) + user_permissions = Permission.objects.filter(codename='change_template') + user.user_permissions.add(*user_permissions) + user.organizations_dict # force caching + self.client.force_login(user) + token = self._obtain_auth_token() + auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + response = self.client.get(reverse('test_template_list'), **auth) + self.assertEqual(response.status_code, 200) diff --git a/tests/testapp/views.py b/tests/testapp/views.py index 716ce61d..f7515ca5 100644 --- a/tests/testapp/views.py +++ b/tests/testapp/views.py @@ -14,6 +14,7 @@ ) from openwisp_users.api.permissions import ( BaseOrganizationPermission, + CustomDjangoModelPermissions, IsOrganizationManager, IsOrganizationMember, IsOrganizationOwner, @@ -169,7 +170,10 @@ def get_queryset(self): class TemplateListCreateView(ListCreateAPIView): serializer_class = TemplateSerializer authentication_classes = (BearerAuthentication,) - permission_classes = (IsOrganizationMember,) + permission_classes = ( + IsOrganizationMember, + CustomDjangoModelPermissions, + ) queryset = Template.objects.all() From 3e9ad535f628e86affc5bb286a8969867c531e54 Mon Sep 17 00:00:00 2001 From: ManishShah120 Date: Sun, 23 May 2021 23:22:11 +0530 Subject: [PATCH 2/5] [tests] Updated tests for ViewDjangoModelPermissions class --- openwisp_users/api/permissions.py | 11 +- tests/testapp/__init__.py | 8 ++ .../testapp/tests/test_permission_classes.py | 116 +++++++++++++++++- tests/testapp/urls.py | 1 + tests/testapp/views.py | 21 +++- 5 files changed, 142 insertions(+), 15 deletions(-) diff --git a/openwisp_users/api/permissions.py b/openwisp_users/api/permissions.py index 8c0c819e..6b4e40cb 100644 --- a/openwisp_users/api/permissions.py +++ b/openwisp_users/api/permissions.py @@ -70,7 +70,7 @@ def validate_membership(self, user, org): return org and (user.is_superuser or user.is_owner(org)) -class CustomDjangoModelPermissions(DjangoModelPermissions): +class ViewDjangoModelPermissions(DjangoModelPermissions): def __init__(self): self.perms_map = copy.deepcopy(self.perms_map) self.perms_map['GET'] = ['%(app_label)s.view_%(model_name)s'] @@ -81,9 +81,8 @@ def has_permission(self, request, view): if getattr(view, '_ignore_model_permissions', False): return True - if not request.user or ( - not request.user.is_authenticated and self.authenticated_users_only - ): + user = request.user + if not user or (not user.is_authenticated and self.authenticated_users_only): return False queryset = self._queryset(view) @@ -91,9 +90,9 @@ def has_permission(self, request, view): change_perm = self.get_required_permissions('PUT', queryset.model) if request.method == 'GET': - if request.user.has_perms(perms) or request.user.has_perms(change_perm): + if user.has_perms(perms) or user.has_perms(change_perm): return True else: return False - return request.user.has_perms(perms) + return user.has_perms(perms) diff --git a/tests/testapp/__init__.py b/tests/testapp/__init__.py index ec5cb839..c364e18d 100644 --- a/tests/testapp/__init__.py +++ b/tests/testapp/__init__.py @@ -14,3 +14,11 @@ def _create_shelf(self, **kwargs): s.full_clean() s.save() return s + + def _create_template(self, **kwargs): + options = dict(name='test-template') + options.update(kwargs) + t = self.template_model(**options) + t.full_clean() + t.save() + return t diff --git a/tests/testapp/tests/test_permission_classes.py b/tests/testapp/tests/test_permission_classes.py index 2739fecf..844aeace 100644 --- a/tests/testapp/tests/test_permission_classes.py +++ b/tests/testapp/tests/test_permission_classes.py @@ -2,17 +2,22 @@ from django.contrib.auth.models import Permission from django.test import TestCase from django.urls import reverse +from swapper import load_model from openwisp_users.api.throttling import AuthRateThrottle +from ..models import Template from .mixins import TestMultitenancyMixin User = get_user_model() +Group = load_model('openwisp_users', 'Group') +OrganizationUser = load_model('openwisp_users', 'OrganizationUser') class TestPermissionClasses(TestMultitenancyMixin, TestCase): def setUp(self): AuthRateThrottle.rate = 0 + self.template_model = Template self.member_url = reverse('test_api_member_view') self.manager_url = reverse('test_api_manager_view') self.owner_url = reverse('test_api_owner_view') @@ -122,28 +127,127 @@ def test_organization_field_with_errored_parent(self): self.client.get(reverse('test_error_field_view'), **auth) self.assertIn('Organization not found', str(error.exception)) - def test_custom_django_model_permission_with_view_permission(self): + def test_view_permission_with_operator(self): + user = User.objects.create_user( + username='operator', password='tester', email='operator@test.com' + ) + operator_group = Group.objects.filter(name='Operator') + user.groups.set(operator_group) + org1 = self._get_org() + OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) + self.client.force_login(user) + token = self._obtain_auth_token() + auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + t1 = self._create_template(organization=org1) + with self.subTest('Get Template List'): + response = self.client.get(reverse('test_template_list'), **auth) + self.assertEqual(response.status_code, 403) + with self.subTest('Get Template Detail'): + response = self.client.get( + reverse('test_template_detail', args=[t1.pk]), **auth + ) + self.assertEqual(response.status_code, 403) + + def test_view_permission_with_administrator(self): + user = User.objects.create_user( + username='operator', password='tester', email='operator@test.com' + ) + administrator_group = Group.objects.get(name='Administrator') + change_perm = Permission.objects.get(codename='change_template') + administrator_group.permissions.add(change_perm) + user.groups.add(administrator_group) + org1 = self._get_org() + OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) + self.client.force_login(user) + token = self._obtain_auth_token() + auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + t1 = self._create_template(organization=org1) + with self.subTest('Get Template List'): + response = self.client.get(reverse('test_template_list'), **auth) + self.assertEqual(response.status_code, 200) + with self.subTest('Get Template Detail'): + response = self.client.get( + reverse('test_template_detail', args=[t1.pk]), **auth + ) + self.assertEqual(response.status_code, 200) + permissions = administrator_group.permissions.values_list('codename', flat=True) + self.assertFalse('view_template' in permissions) + self.assertTrue('change_template' in permissions) + + def test_view_permission_with_operator_having_view_perm(self): + user = User.objects.create_user( + username='operator', password='tester', email='operator@test.com' + ) + operator_group = Group.objects.get(name='Operator') + view_perm = Permission.objects.get(codename='view_template') + operator_group.permissions.add(view_perm) + user.groups.add(operator_group) + org1 = self._get_org() + OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) + self.client.force_login(user) + token = self._obtain_auth_token() + auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + t1 = self._create_template(organization=org1) + with self.subTest('Get Template List'): + response = self.client.get(reverse('test_template_list'), **auth) + self.assertEqual(response.status_code, 200) + with self.subTest('Get Template Detail'): + response = self.client.get( + reverse('test_template_detail', args=[t1.pk]), **auth + ) + self.assertEqual(response.status_code, 200) + with self.subTest('Change Template Detail'): + data = {'name': 'change-template'} + response = self.client.patch( + reverse('test_template_detail', args=[t1.pk]), data, **auth + ) + self.assertEqual(response.status_code, 403) + with self.subTest('Delete Template'): + response = self.client.delete( + reverse('test_template_detail', args=[t1.pk]), **auth + ) + self.assertEqual(response.status_code, 403) + + def test_view_django_model_permission_with_view_perm(self): user = User.objects.create_user( username='operator', password='tester', email='operator@test.com' ) user_permissions = Permission.objects.filter(codename='view_template') user.user_permissions.add(*user_permissions) user.organizations_dict # force caching + org1 = self._get_org() + OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) self.client.force_login(user) token = self._obtain_auth_token() auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - response = self.client.get(reverse('test_template_list'), **auth) - self.assertEqual(response.status_code, 200) + t1 = self._create_template(organization=org1) + with self.subTest('Get Template List'): + response = self.client.get(reverse('test_template_list'), **auth) + self.assertEqual(response.status_code, 200) + with self.subTest('Get Template Detail'): + response = self.client.get( + reverse('test_template_detail', args=[t1.pk]), **auth + ) + self.assertEqual(response.status_code, 200) - def test_custom_django_model_permission_with_change_permission(self): + def test_view_django_model_permission_with_change_perm(self): user = User.objects.create_user( username='operator', password='tester', email='operator@test.com' ) user_permissions = Permission.objects.filter(codename='change_template') user.user_permissions.add(*user_permissions) user.organizations_dict # force caching + org1 = self._get_org() + OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) self.client.force_login(user) token = self._obtain_auth_token() auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - response = self.client.get(reverse('test_template_list'), **auth) - self.assertEqual(response.status_code, 200) + t1 = self._create_template(organization=org1) + with self.subTest('Get Template List'): + response = self.client.get(reverse('test_template_list'), **auth) + self.assertEqual(response.status_code, 200) + with self.subTest('Get Template Detail'): + response = self.client.get( + reverse('test_template_detail', args=[t1.pk]), **auth + ) + self.assertEqual(response.status_code, 200) diff --git a/tests/testapp/urls.py b/tests/testapp/urls.py index 625bd022..6a7202ff 100644 --- a/tests/testapp/urls.py +++ b/tests/testapp/urls.py @@ -48,4 +48,5 @@ name='test_shelf_list_unauthorized_view', ), path('template/', views.template_list, name='test_template_list',), + path('template//', views.template_detail, name='test_template_detail',), ] diff --git a/tests/testapp/views.py b/tests/testapp/views.py index f7515ca5..5e1b1c65 100644 --- a/tests/testapp/views.py +++ b/tests/testapp/views.py @@ -1,5 +1,9 @@ import swapper -from rest_framework.generics import ListAPIView, ListCreateAPIView +from rest_framework.generics import ( + ListAPIView, + ListCreateAPIView, + RetrieveUpdateDestroyAPIView, +) from rest_framework.response import Response from rest_framework.views import APIView @@ -14,10 +18,10 @@ ) from openwisp_users.api.permissions import ( BaseOrganizationPermission, - CustomDjangoModelPermissions, IsOrganizationManager, IsOrganizationMember, IsOrganizationOwner, + ViewDjangoModelPermissions, ) from .models import Book, Config, Shelf, Template @@ -172,7 +176,17 @@ class TemplateListCreateView(ListCreateAPIView): authentication_classes = (BearerAuthentication,) permission_classes = ( IsOrganizationMember, - CustomDjangoModelPermissions, + ViewDjangoModelPermissions, + ) + queryset = Template.objects.all() + + +class TemplateDetailView(FilterByOrganizationManaged, RetrieveUpdateDestroyAPIView): + serializer_class = TemplateSerializer + authentication_classes = (BearerAuthentication,) + permission_classes = ( + IsOrganizationMember, + ViewDjangoModelPermissions, ) queryset = Template.objects.all() @@ -192,3 +206,4 @@ class TemplateListCreateView(ListCreateAPIView): shelf_list_manager_view = ShelfListManagerView.as_view() shelf_list_owner_view = ShelfListOwnerView.as_view() template_list = TemplateListCreateView.as_view() +template_detail = TemplateDetailView.as_view() From 2fe5487e2798db8fc27240f1b37168f65fa34efd Mon Sep 17 00:00:00 2001 From: ManishShah120 Date: Thu, 27 May 2021 16:12:31 +0530 Subject: [PATCH 3/5] [change] Set `perms_map` attribute --- openwisp_users/api/permissions.py | 14 +++++++++----- tests/testapp/views.py | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/openwisp_users/api/permissions.py b/openwisp_users/api/permissions.py index 6b4e40cb..774b44da 100644 --- a/openwisp_users/api/permissions.py +++ b/openwisp_users/api/permissions.py @@ -1,5 +1,3 @@ -import copy - from django.utils.translation import gettext_lazy as _ from rest_framework.permissions import BasePermission, DjangoModelPermissions from swapper import load_model @@ -71,9 +69,15 @@ def validate_membership(self, user, org): class ViewDjangoModelPermissions(DjangoModelPermissions): - def __init__(self): - self.perms_map = copy.deepcopy(self.perms_map) - self.perms_map['GET'] = ['%(app_label)s.view_%(model_name)s'] + perms_map = { + 'GET': ['%(app_label)s.view_%(model_name)s'], + 'OPTIONS': [], + 'HEAD': ['%(app_label)s.view_%(model_name)s'], + 'POST': ['%(app_label)s.add_%(model_name)s'], + 'PUT': ['%(app_label)s.change_%(model_name)s'], + 'PATCH': ['%(app_label)s.change_%(model_name)s'], + 'DELETE': ['%(app_label)s.delete_%(model_name)s'], + } def has_permission(self, request, view): # Workaround to ensure DjangoModelPermissions are not applied diff --git a/tests/testapp/views.py b/tests/testapp/views.py index 5e1b1c65..80294f0e 100644 --- a/tests/testapp/views.py +++ b/tests/testapp/views.py @@ -171,7 +171,7 @@ def get_queryset(self): return shelf.book_set.all() -class TemplateListCreateView(ListCreateAPIView): +class TemplateListCreateView(FilterByOrganizationManaged, ListCreateAPIView): serializer_class = TemplateSerializer authentication_classes = (BearerAuthentication,) permission_classes = ( From c446b0d5909b47dbd741ca37bb99a3a6e30e3927 Mon Sep 17 00:00:00 2001 From: ManishShah120 Date: Tue, 1 Jun 2021 16:51:06 +0530 Subject: [PATCH 4/5] [change] Improved code quality --- openwisp_users/api/permissions.py | 12 ++-- .../testapp/tests/test_permission_classes.py | 58 ++++++------------- tests/testapp/views.py | 6 +- 3 files changed, 27 insertions(+), 49 deletions(-) diff --git a/openwisp_users/api/permissions.py b/openwisp_users/api/permissions.py index 774b44da..04c4c6ff 100644 --- a/openwisp_users/api/permissions.py +++ b/openwisp_users/api/permissions.py @@ -1,5 +1,8 @@ from django.utils.translation import gettext_lazy as _ -from rest_framework.permissions import BasePermission, DjangoModelPermissions +from rest_framework.permissions import BasePermission +from rest_framework.permissions import ( + DjangoModelPermissions as BaseDjangoModelPermissions, +) from swapper import load_model Organization = load_model('openwisp_users', 'Organization') @@ -68,7 +71,7 @@ def validate_membership(self, user, org): return org and (user.is_superuser or user.is_owner(org)) -class ViewDjangoModelPermissions(DjangoModelPermissions): +class DjangoModelPermissions(BaseDjangoModelPermissions): perms_map = { 'GET': ['%(app_label)s.view_%(model_name)s'], 'OPTIONS': [], @@ -94,9 +97,6 @@ def has_permission(self, request, view): change_perm = self.get_required_permissions('PUT', queryset.model) if request.method == 'GET': - if user.has_perms(perms) or user.has_perms(change_perm): - return True - else: - return False + return user.has_perms(perms) or user.has_perms(change_perm) return user.has_perms(perms) diff --git a/tests/testapp/tests/test_permission_classes.py b/tests/testapp/tests/test_permission_classes.py index 844aeace..f69cda56 100644 --- a/tests/testapp/tests/test_permission_classes.py +++ b/tests/testapp/tests/test_permission_classes.py @@ -127,18 +127,20 @@ def test_organization_field_with_errored_parent(self): self.client.get(reverse('test_error_field_view'), **auth) self.assertIn('Organization not found', str(error.exception)) - def test_view_permission_with_operator(self): - user = User.objects.create_user( - username='operator', password='tester', email='operator@test.com' - ) - operator_group = Group.objects.filter(name='Operator') - user.groups.set(operator_group) - org1 = self._get_org() + def _get_auth_template(self, user, org1): OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) self.client.force_login(user) - token = self._obtain_auth_token() + token = self._obtain_auth_token(user) auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') t1 = self._create_template(organization=org1) + return (auth, t1) + + def test_view_permission_with_operator(self): + user = self._get_user() + operator_group = Group.objects.filter(name='Operator') + user.groups.set(operator_group) + org1 = self._get_org() + auth, t1 = self._get_auth_template(user, org1) with self.subTest('Get Template List'): response = self.client.get(reverse('test_template_list'), **auth) self.assertEqual(response.status_code, 403) @@ -149,19 +151,13 @@ def test_view_permission_with_operator(self): self.assertEqual(response.status_code, 403) def test_view_permission_with_administrator(self): - user = User.objects.create_user( - username='operator', password='tester', email='operator@test.com' - ) + user = self._get_user() administrator_group = Group.objects.get(name='Administrator') change_perm = Permission.objects.get(codename='change_template') administrator_group.permissions.add(change_perm) user.groups.add(administrator_group) org1 = self._get_org() - OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) - self.client.force_login(user) - token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - t1 = self._create_template(organization=org1) + auth, t1 = self._get_auth_template(user, org1) with self.subTest('Get Template List'): response = self.client.get(reverse('test_template_list'), **auth) self.assertEqual(response.status_code, 200) @@ -175,19 +171,13 @@ def test_view_permission_with_administrator(self): self.assertTrue('change_template' in permissions) def test_view_permission_with_operator_having_view_perm(self): - user = User.objects.create_user( - username='operator', password='tester', email='operator@test.com' - ) + user = self._get_user() operator_group = Group.objects.get(name='Operator') view_perm = Permission.objects.get(codename='view_template') operator_group.permissions.add(view_perm) user.groups.add(operator_group) org1 = self._get_org() - OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) - self.client.force_login(user) - token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - t1 = self._create_template(organization=org1) + auth, t1 = self._get_auth_template(user, org1) with self.subTest('Get Template List'): response = self.client.get(reverse('test_template_list'), **auth) self.assertEqual(response.status_code, 200) @@ -209,18 +199,12 @@ def test_view_permission_with_operator_having_view_perm(self): self.assertEqual(response.status_code, 403) def test_view_django_model_permission_with_view_perm(self): - user = User.objects.create_user( - username='operator', password='tester', email='operator@test.com' - ) + user = self._get_user() user_permissions = Permission.objects.filter(codename='view_template') user.user_permissions.add(*user_permissions) user.organizations_dict # force caching org1 = self._get_org() - OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) - self.client.force_login(user) - token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - t1 = self._create_template(organization=org1) + auth, t1 = self._get_auth_template(user, org1) with self.subTest('Get Template List'): response = self.client.get(reverse('test_template_list'), **auth) self.assertEqual(response.status_code, 200) @@ -231,18 +215,12 @@ def test_view_django_model_permission_with_view_perm(self): self.assertEqual(response.status_code, 200) def test_view_django_model_permission_with_change_perm(self): - user = User.objects.create_user( - username='operator', password='tester', email='operator@test.com' - ) + user = self._get_user() user_permissions = Permission.objects.filter(codename='change_template') user.user_permissions.add(*user_permissions) user.organizations_dict # force caching org1 = self._get_org() - OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) - self.client.force_login(user) - token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - t1 = self._create_template(organization=org1) + auth, t1 = self._get_auth_template(user, org1) with self.subTest('Get Template List'): response = self.client.get(reverse('test_template_list'), **auth) self.assertEqual(response.status_code, 200) diff --git a/tests/testapp/views.py b/tests/testapp/views.py index 80294f0e..4c84c992 100644 --- a/tests/testapp/views.py +++ b/tests/testapp/views.py @@ -18,10 +18,10 @@ ) from openwisp_users.api.permissions import ( BaseOrganizationPermission, + DjangoModelPermissions, IsOrganizationManager, IsOrganizationMember, IsOrganizationOwner, - ViewDjangoModelPermissions, ) from .models import Book, Config, Shelf, Template @@ -176,7 +176,7 @@ class TemplateListCreateView(FilterByOrganizationManaged, ListCreateAPIView): authentication_classes = (BearerAuthentication,) permission_classes = ( IsOrganizationMember, - ViewDjangoModelPermissions, + DjangoModelPermissions, ) queryset = Template.objects.all() @@ -186,7 +186,7 @@ class TemplateDetailView(FilterByOrganizationManaged, RetrieveUpdateDestroyAPIVi authentication_classes = (BearerAuthentication,) permission_classes = ( IsOrganizationMember, - ViewDjangoModelPermissions, + DjangoModelPermissions, ) queryset = Template.objects.all() From b78c4a1dd1a0b7e6044988f0e44eca5e87d2bf54 Mon Sep 17 00:00:00 2001 From: ManishShah120 Date: Tue, 1 Jun 2021 18:57:19 +0530 Subject: [PATCH 5/5] [docs] Added docu for `DjangoModelPermissions` in DRF section --- README.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.rst b/README.rst index a023afd2..3b8a52b0 100644 --- a/README.rst +++ b/README.rst @@ -600,6 +600,27 @@ Ensure the queryset of your views make use of `select_related `_ in these cases to avoid generating too many queries. +``DjangoModelPermissions`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default ``DjangoModelPermissions`` class doesn't checks for the +``view`` permission of any object for ``GET`` requests. The extended +``DjangoModelPermissions`` class overcomes this problem. In order to +allow ``GET`` requests on any object it checks for the availability +of either ``view`` or ``change`` permissions. + +Usage example: + +.. code-block:: python + + from openwisp_users.api.permissions import DjangoModelPermissions + from rest_framework.generics import ListCreateAPIView + + class TemplateListCreateView(ListCreateAPIView): + serializer_class = TemplateSerializer + permission_classes = (DjangoModelPermissions,) + queryset = Template.objects.all() + Django REST Framework Mixins ----------------------------