Skip to content

Commit

Permalink
Replace HttpResponse type with HttpResponseBase
Browse files Browse the repository at this point in the history
  • Loading branch information
robhudson committed Jun 28, 2024
1 parent c2a4317 commit 0afd172
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 38 deletions.
6 changes: 3 additions & 3 deletions csp/contrib/rate_limiting.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
from csp.utils import build_policy

if TYPE_CHECKING:
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest, HttpResponseBase


class RateLimitedCSPMiddleware(CSPMiddleware):
"""A CSP middleware that rate-limits the number of violation reports sent
to report-uri by excluding it from some requests."""

def build_policy(self, request: HttpRequest, response: HttpResponse) -> str:
def build_policy(self, request: HttpRequest, response: HttpResponseBase) -> str:
config = getattr(response, "_csp_config", None)
update = getattr(response, "_csp_update", None)
replace = getattr(response, "_csp_replace", {})
Expand All @@ -33,7 +33,7 @@ def build_policy(self, request: HttpRequest, response: HttpResponse) -> str:

return build_policy(config=config, update=update, replace=replace, nonce=nonce)

def build_policy_ro(self, request: HttpRequest, response: HttpResponse) -> str:
def build_policy_ro(self, request: HttpRequest, response: HttpResponseBase) -> str:
config = getattr(response, "_csp_config_ro", None)
update = getattr(response, "_csp_update_ro", None)
replace = getattr(response, "_csp_replace_ro", {})
Expand Down
22 changes: 13 additions & 9 deletions csp/decorators.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from __future__ import annotations

from functools import wraps
from typing import Callable, Optional, Any, Dict, List
from django.http import HttpRequest, HttpResponse
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional

if TYPE_CHECKING:
from django.http import HttpRequest, HttpResponseBase

# A generic Django view function
_VIEW_T = Callable[[HttpRequest], HttpResponse]
_VIEW_DECORATOR_T = Callable[[_VIEW_T], _VIEW_T]
# A generic Django view function
_VIEW_T = Callable[[HttpRequest], HttpResponseBase]
_VIEW_DECORATOR_T = Callable[[_VIEW_T], _VIEW_T]


def csp_exempt(REPORT_ONLY: Optional[bool] = None) -> _VIEW_DECORATOR_T:
Expand All @@ -18,7 +22,7 @@ def csp_exempt(REPORT_ONLY: Optional[bool] = None) -> _VIEW_DECORATOR_T:

def decorator(f: _VIEW_T) -> _VIEW_T:
@wraps(f)
def _wrapped(*a: Any, **kw: Any) -> HttpResponse:
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
resp = f(*a, **kw)
if REPORT_ONLY:
setattr(resp, "_csp_exempt_ro", True)
Expand All @@ -44,7 +48,7 @@ def csp_update(config: Optional[Dict[str, Any]] = None, REPORT_ONLY: bool = Fals

def decorator(f: _VIEW_T) -> _VIEW_T:
@wraps(f)
def _wrapped(*a: Any, **kw: Any) -> HttpResponse:
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
resp = f(*a, **kw)
if REPORT_ONLY:
setattr(resp, "_csp_update_ro", config)
Expand All @@ -63,7 +67,7 @@ def csp_replace(config: Optional[Dict[str, Any]] = None, REPORT_ONLY: bool = Fal

def decorator(f: _VIEW_T) -> _VIEW_T:
@wraps(f)
def _wrapped(*a: Any, **kw: Any) -> HttpResponse:
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
resp = f(*a, **kw)
if REPORT_ONLY:
setattr(resp, "_csp_replace_ro", config)
Expand All @@ -87,7 +91,7 @@ def csp(config: Optional[Dict[str, Any]] = None, REPORT_ONLY: bool = False, **kw

def decorator(f: _VIEW_T) -> _VIEW_T:
@wraps(f)
def _wrapped(*a: Any, **kw: Any) -> HttpResponse:
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
resp = f(*a, **kw)
if REPORT_ONLY:
setattr(resp, "_csp_config_ro", processed_config)
Expand Down
8 changes: 4 additions & 4 deletions csp/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from csp.utils import build_policy

if TYPE_CHECKING:
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest, HttpResponseBase


class CSPMiddleware(MiddlewareMixin):
Expand All @@ -39,7 +39,7 @@ def process_request(self, request: HttpRequest) -> None:
nonce = partial(self._make_nonce, request)
setattr(request, "csp_nonce", SimpleLazyObject(nonce))

def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse:
def process_response(self, request: HttpRequest, response: HttpResponseBase) -> HttpResponseBase:
# Check for debug view
exempted_debug_codes = (
http_client.INTERNAL_SERVER_ERROR,
Expand Down Expand Up @@ -72,14 +72,14 @@ def process_response(self, request: HttpRequest, response: HttpResponse) -> Http

return response

def build_policy(self, request: HttpRequest, response: HttpResponse) -> str:
def build_policy(self, request: HttpRequest, response: HttpResponseBase) -> str:
config = getattr(response, "_csp_config", None)
update = getattr(response, "_csp_update", None)
replace = getattr(response, "_csp_replace", None)
nonce = getattr(request, "_csp_nonce", None)
return build_policy(config=config, update=update, replace=replace, nonce=nonce)

def build_policy_ro(self, request: HttpRequest, response: HttpResponse) -> str:
def build_policy_ro(self, request: HttpRequest, response: HttpResponseBase) -> str:
config = getattr(response, "_csp_config_ro", None)
update = getattr(response, "_csp_update_ro", None)
replace = getattr(response, "_csp_replace_ro", None)
Expand Down
44 changes: 22 additions & 22 deletions csp/tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
from csp.tests.utils import response

if TYPE_CHECKING:
from django.http import HttpRequest
from django.http import HttpRequest, HttpResponseBase

mw = CSPMiddleware(response())


def test_csp_exempt() -> None:
@csp_exempt()
def view(request: HttpRequest) -> HttpResponse:
def view(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view(RequestFactory().get("/"))
Expand All @@ -29,7 +29,7 @@ def view(request: HttpRequest) -> HttpResponse:

def test_csp_exempt_ro() -> None:
@csp_exempt(REPORT_ONLY=True)
def view(request: HttpRequest) -> HttpResponse:
def view(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view(RequestFactory().get("/"))
Expand All @@ -41,7 +41,7 @@ def view(request: HttpRequest) -> HttpResponse:
def test_csp_update() -> None:
request = RequestFactory().get("/")

def view_without_decorator(request: HttpRequest) -> HttpResponse:
def view_without_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_without_decorator(request)
Expand All @@ -51,7 +51,7 @@ def view_without_decorator(request: HttpRequest) -> HttpResponse:
assert policy_list == ["default-src 'self'", "img-src foo.com"]

@csp_update({"img-src": ["bar.com", NONCE]})
def view_with_decorator(request: HttpRequest) -> HttpResponse:
def view_with_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_with_decorator(request)
Expand All @@ -74,7 +74,7 @@ def view_with_decorator(request: HttpRequest) -> HttpResponse:
def test_csp_update_ro() -> None:
request = RequestFactory().get("/")

def view_without_decorator(request: HttpRequest) -> HttpResponse:
def view_without_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_without_decorator(request)
Expand All @@ -84,7 +84,7 @@ def view_without_decorator(request: HttpRequest) -> HttpResponse:
assert policy_list == ["default-src 'self'", "img-src foo.com"]

@csp_update({"img-src": ["bar.com", NONCE]}, REPORT_ONLY=True)
def view_with_decorator(request: HttpRequest) -> HttpResponse:
def view_with_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_with_decorator(request)
Expand All @@ -107,7 +107,7 @@ def view_with_decorator(request: HttpRequest) -> HttpResponse:
def test_csp_replace() -> None:
request = RequestFactory().get("/")

def view_without_decorator(request: HttpRequest) -> HttpResponse:
def view_without_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_without_decorator(request)
Expand All @@ -117,7 +117,7 @@ def view_without_decorator(request: HttpRequest) -> HttpResponse:
assert policy_list == ["default-src 'self'", "img-src foo.com"]

@csp_replace({"img-src": ["bar.com"]})
def view_with_decorator(request: HttpRequest) -> HttpResponse:
def view_with_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_with_decorator(request)
Expand All @@ -134,7 +134,7 @@ def view_with_decorator(request: HttpRequest) -> HttpResponse:
assert policy_list == ["default-src 'self'", "img-src foo.com"]

@csp_replace({"img-src": None})
def view_removing_directive(request: HttpRequest) -> HttpResponse:
def view_removing_directive(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_removing_directive(request)
Expand All @@ -148,7 +148,7 @@ def view_removing_directive(request: HttpRequest) -> HttpResponse:
def test_csp_replace_ro() -> None:
request = RequestFactory().get("/")

def view_without_decorator(request: HttpRequest) -> HttpResponse:
def view_without_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_without_decorator(request)
Expand All @@ -158,7 +158,7 @@ def view_without_decorator(request: HttpRequest) -> HttpResponse:
assert policy_list == ["default-src 'self'", "img-src foo.com"]

@csp_replace({"img-src": ["bar.com"]}, REPORT_ONLY=True)
def view_with_decorator(request: HttpRequest) -> HttpResponse:
def view_with_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_with_decorator(request)
Expand All @@ -175,7 +175,7 @@ def view_with_decorator(request: HttpRequest) -> HttpResponse:
assert policy_list == ["default-src 'self'", "img-src foo.com"]

@csp_replace({"img-src": None}, REPORT_ONLY=True)
def view_removing_directive(request: HttpRequest) -> HttpResponse:
def view_removing_directive(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_removing_directive(request)
Expand All @@ -188,7 +188,7 @@ def view_removing_directive(request: HttpRequest) -> HttpResponse:
def test_csp() -> None:
request = RequestFactory().get("/")

def view_without_decorator(request: HttpRequest) -> HttpResponse:
def view_without_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_without_decorator(request)
Expand All @@ -198,7 +198,7 @@ def view_without_decorator(request: HttpRequest) -> HttpResponse:
assert policy_list == ["default-src 'self'"]

@csp({"img-src": ["foo.com"], "font-src": ["bar.com"]})
def view_with_decorator(request: HttpRequest) -> HttpResponse:
def view_with_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_with_decorator(request)
Expand All @@ -218,7 +218,7 @@ def view_with_decorator(request: HttpRequest) -> HttpResponse:
def test_csp_ro() -> None:
request = RequestFactory().get("/")

def view_without_decorator(request: HttpRequest) -> HttpResponse:
def view_without_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_without_decorator(request)
Expand All @@ -229,7 +229,7 @@ def view_without_decorator(request: HttpRequest) -> HttpResponse:

@csp({"img-src": ["foo.com"], "font-src": ["bar.com"]}, REPORT_ONLY=True)
@csp({}) # CSP with no directives effectively removes the header.
def view_with_decorator(request: HttpRequest) -> HttpResponse:
def view_with_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_with_decorator(request)
Expand All @@ -251,7 +251,7 @@ def test_csp_string_values() -> None:
request = RequestFactory().get("/")

@csp({"img-src": "foo.com", "font-src": "bar.com"})
def view_with_decorator(request: HttpRequest) -> HttpResponse:
def view_with_decorator(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

response = view_with_decorator(request)
Expand All @@ -268,7 +268,7 @@ def test_csp_exempt_error() -> None:
with pytest.raises(RuntimeError) as excinfo:
# Ignore type error since we're checking for the exception raised for 3.x syntax
@csp_exempt # type: ignore
def view(request: HttpRequest) -> HttpResponse:
def view(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

assert "Incompatible `csp_exempt` decorator usage" in str(excinfo.value)
Expand All @@ -278,7 +278,7 @@ def test_csp_update_error() -> None:
with pytest.raises(RuntimeError) as excinfo:

@csp_update(IMG_SRC="bar.com")
def view(request: HttpRequest) -> HttpResponse:
def view(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

assert "Incompatible `csp_update` decorator arguments" in str(excinfo.value)
Expand All @@ -288,7 +288,7 @@ def test_csp_replace_error() -> None:
with pytest.raises(RuntimeError) as excinfo:

@csp_replace(IMG_SRC="bar.com")
def view(request: HttpRequest) -> HttpResponse:
def view(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

assert "Incompatible `csp_replace` decorator arguments" in str(excinfo.value)
Expand All @@ -298,7 +298,7 @@ def test_csp_error() -> None:
with pytest.raises(RuntimeError) as excinfo:

@csp(IMG_SRC=["bar.com"])
def view(request: HttpRequest) -> HttpResponse:
def view(request: HttpRequest) -> HttpResponseBase:
return HttpResponse()

assert "Incompatible `csp` decorator arguments" in str(excinfo.value)

0 comments on commit 0afd172

Please sign in to comment.