зеркало из https://github.com/mozilla/django-csp.git
105 строки
3.4 KiB
Python
105 строки
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
from functools import wraps
|
|
from typing import TYPE_CHECKING, Any, Callable
|
|
|
|
if TYPE_CHECKING:
|
|
from django.http import HttpRequest, HttpResponseBase
|
|
|
|
# A generic Django view function
|
|
_VIEW_T = Callable[[HttpRequest], HttpResponseBase]
|
|
_VIEW_DECORATOR_T = Callable[[_VIEW_T], _VIEW_T]
|
|
|
|
|
|
def csp_exempt(REPORT_ONLY: bool | None = None) -> _VIEW_DECORATOR_T:
|
|
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: _VIEW_T) -> _VIEW_T:
|
|
@wraps(f)
|
|
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
|
|
resp = f(*a, **kw)
|
|
if REPORT_ONLY:
|
|
setattr(resp, "_csp_exempt_ro", True)
|
|
else:
|
|
setattr(resp, "_csp_exempt", True)
|
|
return resp
|
|
|
|
return _wrapped
|
|
|
|
return decorator
|
|
|
|
|
|
# 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: dict[str, Any] | None = None, REPORT_ONLY: bool = False, **kwargs: Any) -> _VIEW_DECORATOR_T:
|
|
if config is None and kwargs:
|
|
raise RuntimeError(DECORATOR_DEPRECATION_ERROR.format(fname="csp_update"))
|
|
|
|
def decorator(f: _VIEW_T) -> _VIEW_T:
|
|
@wraps(f)
|
|
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
|
|
resp = f(*a, **kw)
|
|
if REPORT_ONLY:
|
|
setattr(resp, "_csp_update_ro", config)
|
|
else:
|
|
setattr(resp, "_csp_update", config)
|
|
return resp
|
|
|
|
return _wrapped
|
|
|
|
return decorator
|
|
|
|
|
|
def csp_replace(config: dict[str, Any] | None = None, REPORT_ONLY: bool = False, **kwargs: Any) -> _VIEW_DECORATOR_T:
|
|
if config is None and kwargs:
|
|
raise RuntimeError(DECORATOR_DEPRECATION_ERROR.format(fname="csp_replace"))
|
|
|
|
def decorator(f: _VIEW_T) -> _VIEW_T:
|
|
@wraps(f)
|
|
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
|
|
resp = f(*a, **kw)
|
|
if REPORT_ONLY:
|
|
setattr(resp, "_csp_replace_ro", config)
|
|
else:
|
|
setattr(resp, "_csp_replace", config)
|
|
return resp
|
|
|
|
return _wrapped
|
|
|
|
return decorator
|
|
|
|
|
|
def csp(config: dict[str, Any] | None = None, REPORT_ONLY: bool = False, **kwargs: Any) -> _VIEW_DECORATOR_T:
|
|
if config is None and kwargs:
|
|
raise RuntimeError(DECORATOR_DEPRECATION_ERROR.format(fname="csp"))
|
|
|
|
if config is None:
|
|
processed_config: dict[str, list[Any]] = {}
|
|
else:
|
|
processed_config = {k: [v] if isinstance(v, str) else v for k, v in config.items()}
|
|
|
|
def decorator(f: _VIEW_T) -> _VIEW_T:
|
|
@wraps(f)
|
|
def _wrapped(*a: Any, **kw: Any) -> HttpResponseBase:
|
|
resp = f(*a, **kw)
|
|
if REPORT_ONLY:
|
|
setattr(resp, "_csp_config_ro", processed_config)
|
|
else:
|
|
setattr(resp, "_csp_config", processed_config)
|
|
return resp
|
|
|
|
return _wrapped
|
|
|
|
return decorator
|