Skip to content

Commit

Permalink
Show friendly error if decorator usage wasn't updated
Browse files Browse the repository at this point in the history
  • Loading branch information
robhudson committed May 28, 2024
1 parent 3040252 commit 4c12388
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 3 deletions.
30 changes: 27 additions & 3 deletions csp/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@


def csp_exempt(REPORT_ONLY=None):
if callable(REPORT_ONLY):
raise RuntimeError(
"Incompatible `csp_exempt` decorator usage. This decorator now requires arguments, "
"even if none are passed. Change bare decorator usage (@csp_exempt) to parameterized "
"decorator usage (@csp_exempt()). See the django-csp 4.0 migration guide for more "
"information."
)

def decorator(f):
@wraps(f)
def _wrapped(*a, **kw):
Expand All @@ -17,7 +25,17 @@ def _wrapped(*a, **kw):
return decorator


def csp_update(config, *, REPORT_ONLY=False):
# Error message for deprecated decorator arguments.
DECORATOR_DEPRECATION_ERROR = (
"Incompatible `{fname}` decorator arguments. This decorator now takes a single dict argument. "
"See the django-csp 4.0 migration guide for more information."
)


def csp_update(config=None, REPORT_ONLY=False, **kwargs):
if config is None and kwargs:
raise RuntimeError(DECORATOR_DEPRECATION_ERROR.format(fname="csp_update"))

def decorator(f):
@wraps(f)
def _wrapped(*a, **kw):
Expand All @@ -33,7 +51,10 @@ def _wrapped(*a, **kw):
return decorator


def csp_replace(config, *, REPORT_ONLY=False):
def csp_replace(config=None, REPORT_ONLY=False, **kwargs):
if config is None and kwargs:
raise RuntimeError(DECORATOR_DEPRECATION_ERROR.format(fname="csp_replace"))

def decorator(f):
@wraps(f)
def _wrapped(*a, **kw):
Expand All @@ -49,7 +70,10 @@ def _wrapped(*a, **kw):
return decorator


def csp(config, *, REPORT_ONLY=False):
def csp(config=None, REPORT_ONLY=False, **kwargs):
if config is None and kwargs:
raise RuntimeError(DECORATOR_DEPRECATION_ERROR.format(fname="csp"))

config = {k: [v] if isinstance(v, str) else v for k, v in config.items()}

def decorator(f):
Expand Down
43 changes: 43 additions & 0 deletions csp/tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,46 @@ def view_with_decorator(request):
mw.process_response(request, response)
policy_list = sorted(response[HEADER].split("; "))
assert policy_list == ["font-src bar.com", "img-src foo.com"]


# Deprecation tests


def test_csp_exempt_error():
with pytest.raises(RuntimeError) as excinfo:

@csp_exempt
def view(request):
return HttpResponse()

assert "Incompatible `csp_exempt` decorator usage" in str(excinfo.value)


def test_csp_update_error():
with pytest.raises(RuntimeError) as excinfo:

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

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


def test_csp_replace_error():
with pytest.raises(RuntimeError) as excinfo:

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

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


def test_csp_error():
with pytest.raises(RuntimeError) as excinfo:

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

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

0 comments on commit 4c12388

Please sign in to comment.