Skip to content

Commit d4b93e5

Browse files
committed
Raise exception in csrf middleware instead of use CsrfFailureView
1 parent 7a4e3a3 commit d4b93e5

File tree

3 files changed

+18
-73
lines changed

3 files changed

+18
-73
lines changed

plain/plain/csrf/middleware.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
from __future__ import annotations
22

3-
import logging
43
import re
54
from collections.abc import Callable
65
from typing import TYPE_CHECKING
76
from urllib.parse import urlparse
87

8+
from plain.exceptions import PermissionDenied
99
from plain.http import HttpMiddleware
10-
from plain.logs.utils import log_response
1110
from plain.runtime import settings
1211

13-
from .views import CsrfFailureView
14-
1512
if TYPE_CHECKING:
1613
from plain.http import Request, Response
1714

18-
logger = logging.getLogger("plain.security.csrf")
19-
2015

2116
class CsrfViewMiddleware(HttpMiddleware):
2217
"""
@@ -38,10 +33,10 @@ def __init__(self, get_response: Callable[[Request], Response]):
3833
def process_request(self, request: Request) -> Response:
3934
allowed, reason = self.should_allow_request(request)
4035

41-
if allowed:
42-
return self.get_response(request)
43-
else:
44-
return self.reject(request, reason)
36+
if not allowed:
37+
raise PermissionDenied(reason)
38+
39+
return self.get_response(request)
4540

4641
def should_allow_request(self, request: Request) -> tuple[bool, str]:
4742
# 1. Allow safe methods (GET, HEAD, OPTIONS)
@@ -127,17 +122,3 @@ def should_allow_request(self, request: Request) -> tuple[bool, str]:
127122
False,
128123
f"Cross-origin request detected - Origin {origin} does not match Host",
129124
)
130-
131-
def reject(self, request: Request, reason: str) -> Response:
132-
"""Reject a request with a 403 Forbidden response."""
133-
134-
response = CsrfFailureView.as_view()(request, reason=reason)
135-
log_response(
136-
"Forbidden (%s): %s",
137-
reason,
138-
request.path,
139-
response=response,
140-
request=request,
141-
logger=logger,
142-
)
143-
return response

plain/plain/csrf/views.py

Lines changed: 0 additions & 34 deletions
This file was deleted.

plain/tests/test_csrf.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -314,26 +314,24 @@ def test_request_factory_naturally_bypasses_csrf():
314314
)
315315

316316

317-
@patch("plain.csrf.middleware.CsrfViewMiddleware.reject")
318-
def test_middleware_integration_rejected_request(mock_reject):
319-
"""Rejected requests should return 403 response without calling next."""
317+
def test_middleware_integration_rejected_request():
318+
"""Rejected requests should raise PermissionDenied without calling next."""
320319
from unittest.mock import Mock
321320

322-
rf = RequestFactory()
323-
csrf_middleware = CsrfViewMiddleware(lambda request: None)
321+
from plain.exceptions import PermissionDenied
324322

325-
mock_response = Mock()
326-
mock_reject.return_value = mock_response
327-
csrf_middleware.get_response = Mock()
323+
rf = RequestFactory()
324+
mock_get_response = Mock()
325+
csrf_middleware = CsrfViewMiddleware(mock_get_response)
328326

329327
request = rf.post("/test/", headers={"Origin": "https://attacker.com"})
330-
response = csrf_middleware.process_request(request)
331328

332-
# Should not call next middleware
333-
csrf_middleware.get_response.assert_not_called()
329+
# Should raise PermissionDenied
330+
with pytest.raises(PermissionDenied) as exc_info:
331+
csrf_middleware.process_request(request)
334332

335-
# Should call reject method
336-
mock_reject.assert_called_once()
333+
# Should not call next middleware
334+
mock_get_response.assert_not_called()
337335

338-
# Should return the mocked response
339-
assert response == mock_response
336+
# Exception message should contain the reason
337+
assert "Cross-origin request detected" in str(exc_info.value)

0 commit comments

Comments
 (0)