-
-
Notifications
You must be signed in to change notification settings - Fork 33.9k
Expand file tree
/
Copy pathdebug.py
More file actions
147 lines (116 loc) · 5.13 KB
/
debug.py
File metadata and controls
147 lines (116 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import inspect
from functools import wraps
from asgiref.sync import iscoroutinefunction
from django.http import HttpRequest
coroutine_functions_to_sensitive_variables = {}
def sensitive_variables(*variables):
"""
Indicate which variables used in the decorated function are sensitive so
that those variables can later be treated in a special way, for example
by hiding them when logging unhandled exceptions.
Accept two forms:
* with specified variable names:
@sensitive_variables('user', 'password', 'credit_card')
def my_function(user):
password = user.pass_word
credit_card = user.credit_card_number
...
* without any specified variable names, in which case consider all
variables are sensitive:
@sensitive_variables()
def my_function()
...
"""
if len(variables) == 1 and callable(variables[0]):
raise TypeError(
"sensitive_variables() must be called to use it as a decorator, "
"e.g., use @sensitive_variables(), not @sensitive_variables."
)
def decorator(func):
if iscoroutinefunction(func):
sensitive_variables_wrapper = func
wrapped_func = func
while getattr(wrapped_func, "__wrapped__", None) is not None:
wrapped_func = wrapped_func.__wrapped__
try:
file_path = inspect.getfile(wrapped_func)
except TypeError: # Raises for builtins or native functions.
raise ValueError(
f"{func.__name__} cannot safely be wrapped by "
"@sensitive_variables, make it either non-async or defined in a "
"Python file (not a builtin or from a native extension)."
)
else:
# A source file may not be available (e.g. in .pyc-only
# builds), use the first line number instead.
first_line_number = wrapped_func.__code__.co_firstlineno
key = hash(f"{file_path}:{first_line_number}")
if variables:
coroutine_functions_to_sensitive_variables[key] = variables
else:
coroutine_functions_to_sensitive_variables[key] = "__ALL__"
else:
@wraps(func)
def sensitive_variables_wrapper(*func_args, **func_kwargs):
if variables:
sensitive_variables_wrapper.sensitive_variables = variables
else:
sensitive_variables_wrapper.sensitive_variables = "__ALL__"
return func(*func_args, **func_kwargs)
return sensitive_variables_wrapper
return decorator
def sensitive_post_parameters(*parameters):
"""
Indicate which POST parameters used in the decorated view are sensitive,
so that those parameters can later be treated in a special way, for example
by hiding them when logging unhandled exceptions.
Accept two forms:
* with specified parameters:
@sensitive_post_parameters('password', 'credit_card')
def my_view(request):
pw = request.POST['password']
cc = request.POST['credit_card']
...
* without any specified parameters, in which case consider all
variables are sensitive:
@sensitive_post_parameters()
def my_view(request)
...
"""
if len(parameters) == 1 and callable(parameters[0]):
raise TypeError(
"sensitive_post_parameters() must be called to use it as a "
"decorator, e.g., use @sensitive_post_parameters(), not "
"@sensitive_post_parameters."
)
def decorator(view):
if iscoroutinefunction(view):
@wraps(view)
async def sensitive_post_parameters_wrapper(request, *args, **kwargs):
if not isinstance(request, HttpRequest):
raise TypeError(
"sensitive_post_parameters didn't receive an HttpRequest "
"object. If you are decorating a classmethod, make sure to use "
"@method_decorator."
)
if parameters:
request.sensitive_post_parameters = parameters
else:
request.sensitive_post_parameters = "__ALL__"
return await view(request, *args, **kwargs)
else:
@wraps(view)
def sensitive_post_parameters_wrapper(request, *args, **kwargs):
if not isinstance(request, HttpRequest):
raise TypeError(
"sensitive_post_parameters didn't receive an HttpRequest "
"object. If you are decorating a classmethod, make sure to use "
"@method_decorator."
)
if parameters:
request.sensitive_post_parameters = parameters
else:
request.sensitive_post_parameters = "__ALL__"
return view(request, *args, **kwargs)
return sensitive_post_parameters_wrapper
return decorator