mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Template permission fix (#7391)
* Update API permissions for report templates - Allow reading for any authenticated user - Write permissions for staff users * Update unit tests
This commit is contained in:
parent
cdac7465b2
commit
7108bc48bd
@ -142,7 +142,15 @@ class UserMixin:
|
||||
def setUp(self):
|
||||
"""Run setup for individual test methods."""
|
||||
if self.auto_login:
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
self.login()
|
||||
|
||||
def login(self):
|
||||
"""Login with the current user credentials."""
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
|
||||
def logout(self):
|
||||
"""Lougout current user."""
|
||||
self.client.logout()
|
||||
|
||||
@classmethod
|
||||
def assignRole(cls, role=None, assign_all: bool = False, group=None):
|
||||
|
@ -18,6 +18,7 @@ from rest_framework.response import Response
|
||||
import common.models
|
||||
import InvenTree.exceptions
|
||||
import InvenTree.helpers
|
||||
import InvenTree.permissions
|
||||
import report.helpers
|
||||
import report.models
|
||||
import report.serializers
|
||||
@ -34,6 +35,16 @@ from plugin.builtin.labels.inventree_label import InvenTreeLabelPlugin
|
||||
from plugin.registry import registry
|
||||
|
||||
|
||||
class TemplatePermissionMixin:
|
||||
"""Permission mixin for report and label templates."""
|
||||
|
||||
# Read only for non-staff users
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
InvenTree.permissions.IsStaffOrReadOnly,
|
||||
]
|
||||
|
||||
|
||||
@method_decorator(cache_page(5), name='dispatch')
|
||||
class TemplatePrintBase(RetrieveAPI):
|
||||
"""Base class for printing against templates."""
|
||||
@ -143,6 +154,7 @@ class LabelFilter(ReportFilterBase):
|
||||
class LabelPrint(GenericAPIView):
|
||||
"""API endpoint for printing labels."""
|
||||
|
||||
# Any authenticated user can print labels
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = report.serializers.LabelPrintSerializer
|
||||
|
||||
@ -277,7 +289,7 @@ class LabelPrint(GenericAPIView):
|
||||
)
|
||||
|
||||
|
||||
class LabelTemplateList(ListCreateAPI):
|
||||
class LabelTemplateList(TemplatePermissionMixin, ListCreateAPI):
|
||||
"""API endpoint for viewing list of LabelTemplate objects."""
|
||||
|
||||
queryset = report.models.LabelTemplate.objects.all()
|
||||
@ -288,7 +300,7 @@ class LabelTemplateList(ListCreateAPI):
|
||||
ordering_fields = ['name', 'enabled']
|
||||
|
||||
|
||||
class LabelTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||
class LabelTemplateDetail(TemplatePermissionMixin, RetrieveUpdateDestroyAPI):
|
||||
"""Detail API endpoint for label template model."""
|
||||
|
||||
queryset = report.models.LabelTemplate.objects.all()
|
||||
@ -298,6 +310,7 @@ class LabelTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||
class ReportPrint(GenericAPIView):
|
||||
"""API endpoint for printing reports."""
|
||||
|
||||
# Any authenticated user can print reports
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
serializer_class = report.serializers.ReportPrintSerializer
|
||||
|
||||
@ -434,7 +447,7 @@ class ReportPrint(GenericAPIView):
|
||||
)
|
||||
|
||||
|
||||
class ReportTemplateList(ListCreateAPI):
|
||||
class ReportTemplateList(TemplatePermissionMixin, ListCreateAPI):
|
||||
"""API endpoint for viewing list of ReportTemplate objects."""
|
||||
|
||||
queryset = report.models.ReportTemplate.objects.all()
|
||||
@ -445,49 +458,49 @@ class ReportTemplateList(ListCreateAPI):
|
||||
ordering_fields = ['name', 'enabled']
|
||||
|
||||
|
||||
class ReportTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||
class ReportTemplateDetail(TemplatePermissionMixin, RetrieveUpdateDestroyAPI):
|
||||
"""Detail API endpoint for report template model."""
|
||||
|
||||
queryset = report.models.ReportTemplate.objects.all()
|
||||
serializer_class = report.serializers.ReportTemplateSerializer
|
||||
|
||||
|
||||
class ReportSnippetList(ListCreateAPI):
|
||||
class ReportSnippetList(TemplatePermissionMixin, ListCreateAPI):
|
||||
"""API endpoint for listing ReportSnippet objects."""
|
||||
|
||||
queryset = report.models.ReportSnippet.objects.all()
|
||||
serializer_class = report.serializers.ReportSnippetSerializer
|
||||
|
||||
|
||||
class ReportSnippetDetail(RetrieveUpdateDestroyAPI):
|
||||
class ReportSnippetDetail(TemplatePermissionMixin, RetrieveUpdateDestroyAPI):
|
||||
"""API endpoint for a single ReportSnippet object."""
|
||||
|
||||
queryset = report.models.ReportSnippet.objects.all()
|
||||
serializer_class = report.serializers.ReportSnippetSerializer
|
||||
|
||||
|
||||
class ReportAssetList(ListCreateAPI):
|
||||
class ReportAssetList(TemplatePermissionMixin, ListCreateAPI):
|
||||
"""API endpoint for listing ReportAsset objects."""
|
||||
|
||||
queryset = report.models.ReportAsset.objects.all()
|
||||
serializer_class = report.serializers.ReportAssetSerializer
|
||||
|
||||
|
||||
class ReportAssetDetail(RetrieveUpdateDestroyAPI):
|
||||
class ReportAssetDetail(TemplatePermissionMixin, RetrieveUpdateDestroyAPI):
|
||||
"""API endpoint for a single ReportAsset object."""
|
||||
|
||||
queryset = report.models.ReportAsset.objects.all()
|
||||
serializer_class = report.serializers.ReportAssetSerializer
|
||||
|
||||
|
||||
class LabelOutputList(BulkDeleteMixin, ListAPI):
|
||||
class LabelOutputList(TemplatePermissionMixin, BulkDeleteMixin, ListAPI):
|
||||
"""List endpoint for LabelOutput objects."""
|
||||
|
||||
queryset = report.models.LabelOutput.objects.all()
|
||||
serializer_class = report.serializers.LabelOutputSerializer
|
||||
|
||||
|
||||
class ReportOutputList(BulkDeleteMixin, ListAPI):
|
||||
class ReportOutputList(TemplatePermissionMixin, BulkDeleteMixin, ListAPI):
|
||||
"""List endpoint for ReportOutput objects."""
|
||||
|
||||
queryset = report.models.ReportOutput.objects.all()
|
||||
|
@ -403,6 +403,61 @@ class ReportTest(InvenTreeAPITestCase):
|
||||
|
||||
self.assertEqual(len(p.metadata.keys()), 4)
|
||||
|
||||
def test_report_template_permissions(self):
|
||||
"""Test that the user permissions are correctly applied.
|
||||
|
||||
- For all /api/report/ endpoints, any authenticated user should have full read access
|
||||
- Write access is limited to staff users
|
||||
- Non authenticated users should have no access at all
|
||||
"""
|
||||
# First test the "report list" endpoint
|
||||
url = reverse('api-report-template-list')
|
||||
|
||||
template = ReportTemplate.objects.first()
|
||||
|
||||
detail_url = reverse('api-report-template-detail', kwargs={'pk': template.pk})
|
||||
|
||||
# Non-authenticated user should have no access
|
||||
self.logout()
|
||||
|
||||
self.get(url, expected_code=401)
|
||||
|
||||
# Authenticated user should have read access
|
||||
self.user.is_staff = False
|
||||
self.user.save()
|
||||
|
||||
self.login()
|
||||
|
||||
# Check read access to template list URL
|
||||
self.get(url, expected_code=200)
|
||||
|
||||
# Check read access to template detail URL
|
||||
self.get(detail_url, expected_code=200)
|
||||
|
||||
# An update to the report template should fail
|
||||
self.patch(
|
||||
detail_url,
|
||||
data={'description': 'Some new description here?'},
|
||||
expected_code=403,
|
||||
)
|
||||
|
||||
# Now, test with a staff user
|
||||
self.logout()
|
||||
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
|
||||
self.login()
|
||||
|
||||
self.patch(
|
||||
detail_url,
|
||||
data={'description': 'An updated description'},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
template.refresh_from_db()
|
||||
self.assertEqual(template.description, 'An updated description')
|
||||
|
||||
|
||||
class PrintTestMixins:
|
||||
"""Mixin that enables e2e printing tests."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user