Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/adal/log.py: 26%

58 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:05 +0000

1#------------------------------------------------------------------------------ 

2# 

3# Copyright (c) Microsoft Corporation.  

4# All rights reserved. 

5#  

6# This code is licensed under the MIT License. 

7#  

8# Permission is hereby granted, free of charge, to any person obtaining a copy 

9# of this software and associated documentation files(the "Software"), to deal 

10# in the Software without restriction, including without limitation the rights 

11# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 

12# copies of the Software, and to permit persons to whom the Software is 

13# furnished to do so, subject to the following conditions : 

14#  

15# The above copyright notice and this permission notice shall be included in 

16# all copies or substantial portions of the Software. 

17#  

18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 

21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

23# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

24# THE SOFTWARE. 

25# 

26#------------------------------------------------------------------------------ 

27 

28import logging 

29import uuid 

30import traceback 

31 

32ADAL_LOGGER_NAME = 'adal-python' 

33 

34def create_log_context(correlation_id=None, enable_pii=False): 

35 return { 

36 'correlation_id' : correlation_id or str(uuid.uuid4()), 

37 'enable_pii': enable_pii} 

38 

39def set_logging_options(options=None): 

40 '''Configure adal logger, including level and handler spec'd by python 

41 logging module. 

42 

43 Basic Usages:: 

44 >>>adal.set_logging_options({ 

45 >>> 'level': 'DEBUG', 

46 >>> 'handler': logging.FileHandler('adal.log') 

47 >>>}) 

48 ''' 

49 if options is None: 

50 options = {} 

51 logger = logging.getLogger(ADAL_LOGGER_NAME) 

52 

53 logger.setLevel(options.get('level', logging.ERROR)) 

54 

55 handler = options.get('handler') 

56 if handler: 

57 handler.setLevel(logger.level) 

58 logger.addHandler(handler) 

59 

60def get_logging_options(): 

61 '''Get logging options 

62 

63 :returns: a dict, with a key of 'level' for logging level. 

64 ''' 

65 logger = logging.getLogger(ADAL_LOGGER_NAME) 

66 level = logger.getEffectiveLevel() 

67 return { 

68 'level': logging.getLevelName(level) 

69 } 

70 

71class Logger(object): 

72 '''wrapper around python built-in logging to log correlation_id, and stack 

73 trace through keyword argument of 'log_stack_trace' 

74 ''' 

75 def __init__(self, component_name, log_context): 

76 

77 if not log_context: 

78 raise AttributeError('Logger: log_context is a required parameter') 

79 

80 self._component_name = component_name 

81 self.log_context = log_context 

82 self._logging = logging.getLogger(ADAL_LOGGER_NAME) 

83 

84 def _log_message(self, msg, log_stack_trace=None): 

85 correlation_id = self.log_context.get("correlation_id", 

86 "<no correlation id>") 

87 

88 formatted = "{} - {}:{}".format( 

89 correlation_id, 

90 self._component_name, 

91 msg) 

92 if log_stack_trace: 

93 formatted += "\nStack:\n{}".format(traceback.format_stack()) 

94 

95 return formatted 

96 

97 def warn(self, msg, *args, **kwargs): 

98 """ 

99 The recommended way to call this function with variable content, 

100 is to use the `warn("hello %(name)s", {"name": "John Doe"}` form, 

101 so that this method will scrub pii value when needed. 

102 """ 

103 if len(args) == 1 and isinstance(args[0], dict) and not self.log_context.get('enable_pii'): 

104 args = (scrub_pii(args[0]),) 

105 log_stack_trace = kwargs.pop('log_stack_trace', None) 

106 msg = self._log_message(msg, log_stack_trace) 

107 self._logging.warning(msg, *args, **kwargs) 

108 

109 def info(self, msg, *args, **kwargs): 

110 if len(args) == 1 and isinstance(args[0], dict) and not self.log_context.get('enable_pii'): 

111 args = (scrub_pii(args[0]),) 

112 log_stack_trace = kwargs.pop('log_stack_trace', None) 

113 msg = self._log_message(msg, log_stack_trace) 

114 self._logging.info(msg, *args, **kwargs) 

115 

116 def debug(self, msg, *args, **kwargs): 

117 if len(args) == 1 and isinstance(args[0], dict) and not self.log_context.get('enable_pii'): 

118 args = (scrub_pii(args[0]),) 

119 log_stack_trace = kwargs.pop('log_stack_trace', None) 

120 msg = self._log_message(msg, log_stack_trace) 

121 self._logging.debug(msg, *args, **kwargs) 

122 

123 def exception(self, msg, *args, **kwargs): 

124 if len(args) == 1 and isinstance(args[0], dict) and not self.log_context.get('enable_pii'): 

125 args = (scrub_pii(args[0]),) 

126 msg = self._log_message(msg) 

127 self._logging.exception(msg, *args, **kwargs) 

128 

129 

130def scrub_pii(arg_dict, padding="..."): 

131 """ 

132 The input is a dict with semantic keys, 

133 and the output will be a dict with PII values replaced by padding. 

134 """ 

135 pii = set([ # Personally Identifiable Information 

136 "subject", 

137 "upn", # i.e. user name 

138 "given_name", "family_name", 

139 "email", 

140 "oid", # Object ID 

141 "userid", # Used in ADAL Python token cache 

142 "login_hint", 

143 "home_oid", 

144 "access_token", "refresh_token", "id_token", "token_response", 

145 

146 # The following are actually Organizationally Identifiable Info 

147 "tenant_id", 

148 "authority", # which typically contains tenant_id 

149 "client_id", 

150 "_clientid", # This is the key name ADAL uses in cache query 

151 "redirect_uri", 

152 

153 # Unintuitively, the following can contain PII 

154 "user_realm_url", # e.g. https://login.microsoftonline.com/common/UserRealm/{username} 

155 ]) 

156 return {k: padding if k.lower() in pii else arg_dict[k] for k in arg_dict} 

157