mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-10 06:37:17 +00:00
* Enhanced error message on CSRF failure
* Defer to 'detail' field if exists
(cherry picked from commit 9a416df5d4)
Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
co-authored by
Oliver
parent
1368b7db08
commit
5b97accbf3
@@ -110,7 +110,9 @@ def csrf_failure(request, reason=''):
|
|||||||
"""Custom CSRF failure handler.
|
"""Custom CSRF failure handler.
|
||||||
|
|
||||||
Returns a JSON response for API/headless requests so the frontend can
|
Returns a JSON response for API/headless requests so the frontend can
|
||||||
provide a meaningful error message to the user
|
provide a meaningful error message to the user.
|
||||||
|
|
||||||
|
This Includes Django's own message (e.g. "CSRF cookie not set.") alongside the generic hint.
|
||||||
"""
|
"""
|
||||||
from django.views.csrf import csrf_failure as django_default
|
from django.views.csrf import csrf_failure as django_default
|
||||||
|
|
||||||
@@ -120,14 +122,14 @@ def csrf_failure(request, reason=''):
|
|||||||
or 'application/json' in request.headers.get('Accept', '')
|
or 'application/json' in request.headers.get('Accept', '')
|
||||||
or 'application/json' in request.headers.get('Content-Type', '')
|
or 'application/json' in request.headers.get('Content-Type', '')
|
||||||
):
|
):
|
||||||
return JsonResponse(
|
detail = _(
|
||||||
{
|
|
||||||
'detail': _(
|
|
||||||
'CSRF verification failed. Ensure INVENTREE_SITE_URL and INVENTREE_TRUSTED_ORIGINS are configured correctly.'
|
'CSRF verification failed. Ensure INVENTREE_SITE_URL and INVENTREE_TRUSTED_ORIGINS are configured correctly.'
|
||||||
)
|
)
|
||||||
},
|
|
||||||
status=403,
|
if reason:
|
||||||
)
|
detail = f'{detail} ({reason})'
|
||||||
|
|
||||||
|
return JsonResponse({'detail': detail}, status=403)
|
||||||
|
|
||||||
return django_default(request, reason=reason)
|
return django_default(request, reason=reason)
|
||||||
|
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ class MiddlewareTests(InvenTreeTestCase):
|
|||||||
request.META[f'HTTP_{key.upper().replace("-", "_")}'] = value
|
request.META[f'HTTP_{key.upper().replace("-", "_")}'] = value
|
||||||
return request
|
return request
|
||||||
|
|
||||||
# API path -> JSON 403 with meaningful message
|
# API path -> JSON 403 with meaningful message, plus Django's own reason
|
||||||
response = csrf_failure(
|
response = csrf_failure(
|
||||||
make_request('/api/part/'), reason='origin check failed'
|
make_request('/api/part/'), reason='origin check failed'
|
||||||
)
|
)
|
||||||
@@ -318,9 +318,9 @@ class MiddlewareTests(InvenTreeTestCase):
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertEqual(data['detail'], EXPECTED_DETAIL)
|
self.assertEqual(data['detail'], f'{EXPECTED_DETAIL} (origin check failed)')
|
||||||
|
|
||||||
# allauth headless path -> JSON 403
|
# allauth headless path -> JSON 403 (no reason supplied -> generic message only)
|
||||||
response = csrf_failure(make_request('/_allauth/browser/v1/auth/login'))
|
response = csrf_failure(make_request('/_allauth/browser/v1/auth/login'))
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
@@ -333,16 +333,16 @@ class MiddlewareTests(InvenTreeTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertEqual(data['detail'], EXPECTED_DETAIL)
|
self.assertEqual(data['detail'], f'{EXPECTED_DETAIL} (origin check failed)')
|
||||||
|
|
||||||
# Content-Type: application/json header -> JSON 403
|
# Content-Type: application/json header -> JSON 403
|
||||||
response = csrf_failure(
|
response = csrf_failure(
|
||||||
make_request('/some/other/path/', {'Content-Type': 'application/json'}),
|
make_request('/some/other/path/', {'Content-Type': 'application/json'}),
|
||||||
reason='origin check failed',
|
reason='CSRF cookie not set.',
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertEqual(data['detail'], EXPECTED_DETAIL)
|
self.assertEqual(data['detail'], f'{EXPECTED_DETAIL} (CSRF cookie not set.)')
|
||||||
|
|
||||||
# Plain browser request -> falls back to Django default CSRF page (403 HTML, not JSON)
|
# Plain browser request -> falls back to Django default CSRF page (403 HTML, not JSON)
|
||||||
response = csrf_failure(make_request('/web/'), reason='origin check failed')
|
response = csrf_failure(make_request('/web/'), reason='origin check failed')
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ export function extractErrorMessage({
|
|||||||
let message = '';
|
let message = '';
|
||||||
|
|
||||||
if (error_data) {
|
if (error_data) {
|
||||||
message = error_data[field ?? 'error'] ?? error_data['non_field_errors'];
|
message =
|
||||||
|
error_data[field ?? 'error'] ??
|
||||||
|
error_data['detail'] ??
|
||||||
|
error_data['non_field_errors'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// No message? Look at the response status codes
|
// No message? Look at the response status codes
|
||||||
|
|||||||
Reference in New Issue
Block a user