Permissions fix for Calendar views (#12764)

This commit is contained in:
Oliver
2026-09-02 14:47:34 +10:00
committed by GitHub
parent b03d3b1ba0
commit 1238daa783
2 changed files with 91 additions and 0 deletions
+31
View File
@@ -71,6 +71,7 @@ from order.status_codes import (
)
from part.models import Part
from users.models import Owner
from users.permissions import check_user_permission
order_router = InvenTreeApiRouter()
@@ -2497,6 +2498,32 @@ class OrderCalendarExport(ICalFeed):
timezone = settings.TIME_ZONE
file_name = 'calendar.ics'
# Map the URL 'ordertype' kwarg to the corresponding order model,
# so that access can be checked against the matching RuleSet
ORDER_MODELS = {
'purchase-order': models.PurchaseOrder,
'sales-order': models.SalesOrder,
'return-order': models.ReturnOrder,
'transfer-order': models.TransferOrder,
}
def check_permission(self, request, **kwargs):
"""Check that the requesting user has 'view' permission for the requested order type.
Returns a 403 JsonResponse if the user lacks the required RuleSet permission,
or None if access is permitted.
"""
model = self.ORDER_MODELS.get(kwargs.get('ordertype'))
if model is not None and not check_user_permission(request.user, model, 'view'):
response = JsonResponse({
'detail': 'You do not have permission to view this resource.'
})
response.status_code = 403
return response
return None
def __call__(self, request, *args, **kwargs):
"""Overload call in order to check for authentication.
@@ -2513,6 +2540,8 @@ class OrderCalendarExport(ICalFeed):
if request.user.is_authenticated:
# Authenticated on first try - maybe normal browser call?
if forbidden := self.check_permission(request, **kwargs):
return forbidden
return super().__call__(request, *args, **kwargs)
# No login yet - check in headers
@@ -2531,6 +2560,8 @@ class OrderCalendarExport(ICalFeed):
# Check again
if request.user.is_authenticated:
# Authenticated after second try
if forbidden := self.check_permission(request, **kwargs):
return forbidden
return super().__call__(request, *args, **kwargs)
# Still nothing - return Unauth. header with info on how to authenticate
+60
View File
@@ -921,6 +921,21 @@ class PurchaseOrderTest(OrderTest):
)
self.assertEqual(response.status_code, 200)
def test_po_calendar_no_permission(self):
"""Test that an authenticated user without purchase_order view permission is denied."""
self.clearRoles()
response = self.get(
reverse('api-po-so-calendar', kwargs={'ordertype': 'purchase-order'}),
expected_code=403,
format=None,
)
resp_dict = response.json()
self.assertEqual(
resp_dict['detail'], 'You do not have permission to view this resource.'
)
def test_po_custom_status_query_count(self):
"""Test that listing PurchaseOrders with custom statuses does not cause N+1 queries.
@@ -2291,6 +2306,21 @@ class SalesOrderTest(OrderTest):
self.assertGreaterEqual(n_events, 1)
self.assertEqual(number_orders_incl_complete, n_events)
def test_so_calendar_no_permission(self):
"""Test that an authenticated user without sales_order view permission is denied."""
self.clearRoles()
response = self.get(
reverse('api-po-so-calendar', kwargs={'ordertype': 'sales-order'}),
expected_code=403,
format=None,
)
resp_dict = response.json()
self.assertEqual(
resp_dict['detail'], 'You do not have permission to view this resource.'
)
def test_export(self):
"""Test we can export the SalesOrder list."""
set_global_setting(models.SalesOrder.UNLOCK_SETTING, True)
@@ -3793,6 +3823,21 @@ class ReturnOrderTests(InvenTreeAPITestCase):
calendar = Calendar.from_ical(response.content)
self.assertIsInstance(calendar, Calendar)
def test_ro_calendar_no_permission(self):
"""Test that an authenticated user without return_order view permission is denied."""
self.clearRoles()
response = self.get(
reverse('api-po-so-calendar', kwargs={'ordertype': 'return-order'}),
expected_code=403,
format=None,
)
resp_dict = response.json()
self.assertEqual(
resp_dict['detail'], 'You do not have permission to view this resource.'
)
def test_export(self):
"""Test data export for the ReturnOrder API endpoints."""
# Export return orders
@@ -4380,6 +4425,21 @@ class TransferOrderTest(OrderTest):
self.assertGreaterEqual(n_events, 1)
self.assertEqual(number_orders_incl_complete, n_events)
def test_transfer_order_calendar_no_permission(self):
"""Test that an authenticated user without transfer_order view permission is denied."""
self.clearRoles()
response = self.get(
reverse('api-po-so-calendar', kwargs={'ordertype': 'transfer-order'}),
expected_code=403,
format=None,
)
resp_dict = response.json()
self.assertEqual(
resp_dict['detail'], 'You do not have permission to view this resource.'
)
def test_export(self):
"""Test we can export the TransferOrder list."""
n = models.TransferOrder.objects.count()