Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/views/decorators/debug.py: 13%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

54 statements  

1import inspect 

2from functools import wraps 

3 

4from asgiref.sync import iscoroutinefunction 

5 

6from django.http import HttpRequest 

7 

8coroutine_functions_to_sensitive_variables = {} 

9 

10 

11def sensitive_variables(*variables): 

12 """ 

13 Indicate which variables used in the decorated function are sensitive so 

14 that those variables can later be treated in a special way, for example 

15 by hiding them when logging unhandled exceptions. 

16 

17 Accept two forms: 

18 

19 * with specified variable names: 

20 

21 @sensitive_variables('user', 'password', 'credit_card') 

22 def my_function(user): 

23 password = user.pass_word 

24 credit_card = user.credit_card_number 

25 ... 

26 

27 * without any specified variable names, in which case consider all 

28 variables are sensitive: 

29 

30 @sensitive_variables() 

31 def my_function() 

32 ... 

33 """ 

34 if len(variables) == 1 and callable(variables[0]): 

35 raise TypeError( 

36 "sensitive_variables() must be called to use it as a decorator, " 

37 "e.g., use @sensitive_variables(), not @sensitive_variables." 

38 ) 

39 

40 def decorator(func): 

41 if iscoroutinefunction(func): 

42 sensitive_variables_wrapper = func 

43 

44 wrapped_func = func 

45 while getattr(wrapped_func, "__wrapped__", None) is not None: 

46 wrapped_func = wrapped_func.__wrapped__ 

47 

48 try: 

49 file_path = inspect.getfile(wrapped_func) 

50 except TypeError: # Raises for builtins or native functions. 

51 raise ValueError( 

52 f"{func.__name__} cannot safely be wrapped by " 

53 "@sensitive_variables, make it either non-async or defined in a " 

54 "Python file (not a builtin or from a native extension)." 

55 ) 

56 else: 

57 # A source file may not be available (e.g. in .pyc-only builds), 

58 # use the first line number instead. 

59 first_line_number = wrapped_func.__code__.co_firstlineno 

60 key = hash(f"{file_path}:{first_line_number}") 

61 

62 if variables: 

63 coroutine_functions_to_sensitive_variables[key] = variables 

64 else: 

65 coroutine_functions_to_sensitive_variables[key] = "__ALL__" 

66 

67 else: 

68 

69 @wraps(func) 

70 def sensitive_variables_wrapper(*func_args, **func_kwargs): 

71 if variables: 

72 sensitive_variables_wrapper.sensitive_variables = variables 

73 else: 

74 sensitive_variables_wrapper.sensitive_variables = "__ALL__" 

75 return func(*func_args, **func_kwargs) 

76 

77 return sensitive_variables_wrapper 

78 

79 return decorator 

80 

81 

82def sensitive_post_parameters(*parameters): 

83 """ 

84 Indicate which POST parameters used in the decorated view are sensitive, 

85 so that those parameters can later be treated in a special way, for example 

86 by hiding them when logging unhandled exceptions. 

87 

88 Accept two forms: 

89 

90 * with specified parameters: 

91 

92 @sensitive_post_parameters('password', 'credit_card') 

93 def my_view(request): 

94 pw = request.POST['password'] 

95 cc = request.POST['credit_card'] 

96 ... 

97 

98 * without any specified parameters, in which case consider all 

99 variables are sensitive: 

100 

101 @sensitive_post_parameters() 

102 def my_view(request) 

103 ... 

104 """ 

105 if len(parameters) == 1 and callable(parameters[0]): 

106 raise TypeError( 

107 "sensitive_post_parameters() must be called to use it as a " 

108 "decorator, e.g., use @sensitive_post_parameters(), not " 

109 "@sensitive_post_parameters." 

110 ) 

111 

112 def decorator(view): 

113 if iscoroutinefunction(view): 

114 

115 @wraps(view) 

116 async def sensitive_post_parameters_wrapper(request, *args, **kwargs): 

117 if not isinstance(request, HttpRequest): 

118 raise TypeError( 

119 "sensitive_post_parameters didn't receive an HttpRequest " 

120 "object. If you are decorating a classmethod, make sure to use " 

121 "@method_decorator." 

122 ) 

123 if parameters: 

124 request.sensitive_post_parameters = parameters 

125 else: 

126 request.sensitive_post_parameters = "__ALL__" 

127 return await view(request, *args, **kwargs) 

128 

129 else: 

130 

131 @wraps(view) 

132 def sensitive_post_parameters_wrapper(request, *args, **kwargs): 

133 if not isinstance(request, HttpRequest): 

134 raise TypeError( 

135 "sensitive_post_parameters didn't receive an HttpRequest " 

136 "object. If you are decorating a classmethod, make sure to use " 

137 "@method_decorator." 

138 ) 

139 if parameters: 

140 request.sensitive_post_parameters = parameters 

141 else: 

142 request.sensitive_post_parameters = "__ALL__" 

143 return view(request, *args, **kwargs) 

144 

145 return sensitive_post_parameters_wrapper 

146 

147 return decorator