Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

log.py 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. """
  2. """
  3. # Created on 2015.05.01
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2015 - 2018 Giovanni Cannata
  8. #
  9. # This file is part of ldap3.
  10. #
  11. # ldap3 is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU Lesser General Public License as published
  13. # by the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # ldap3 is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Lesser General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Lesser General Public License
  22. # along with ldap3 in the COPYING and COPYING.LESSER files.
  23. # If not, see <http://www.gnu.org/licenses/>.
  24. from logging import getLogger, DEBUG
  25. from copy import deepcopy
  26. from pprint import pformat
  27. from ..protocol.rfc4511 import LDAPMessage
  28. # logging levels
  29. OFF = 0
  30. ERROR = 10
  31. BASIC = 20
  32. PROTOCOL = 30
  33. NETWORK = 40
  34. EXTENDED = 50
  35. _sensitive_lines = ('simple', 'credentials', 'serversaslcreds') # must be a tuple, not a list, lowercase
  36. _sensitive_args = ('simple', 'password', 'sasl_credentials', 'saslcreds', 'server_creds')
  37. _sensitive_attrs = ('userpassword', 'unicodepwd')
  38. _hide_sensitive_data = None
  39. DETAIL_LEVELS = [OFF, ERROR, BASIC, PROTOCOL, NETWORK, EXTENDED]
  40. _max_line_length = 4096
  41. _logging_level = None
  42. _detail_level = None
  43. _logging_encoding = 'ascii'
  44. try:
  45. from logging import NullHandler
  46. except ImportError: # NullHandler not present in Python < 2.7
  47. from logging import Handler
  48. class NullHandler(Handler):
  49. def handle(self, record):
  50. pass
  51. def emit(self, record):
  52. pass
  53. def createLock(self):
  54. self.lock = None
  55. def _strip_sensitive_data_from_dict(d):
  56. if not isinstance(d, dict):
  57. return d
  58. try:
  59. d = deepcopy(d)
  60. except Exception: # if deepcopy goes wrong gives up and returns the dict unchanged
  61. return d
  62. for k in d.keys():
  63. if isinstance(d[k], dict):
  64. d[k] = _strip_sensitive_data_from_dict(d[k])
  65. elif k.lower() in _sensitive_args and d[k]:
  66. d[k] = '<stripped %d characters of sensitive data>' % len(d[k])
  67. return d
  68. def get_detail_level_name(level_name):
  69. if level_name == OFF:
  70. return 'OFF'
  71. elif level_name == ERROR:
  72. return 'ERROR'
  73. elif level_name == BASIC:
  74. return 'BASIC'
  75. elif level_name == PROTOCOL:
  76. return 'PROTOCOL'
  77. elif level_name == NETWORK:
  78. return 'NETWORK'
  79. elif level_name == EXTENDED:
  80. return 'EXTENDED'
  81. raise ValueError('unknown detail level')
  82. def log(detail, message, *args):
  83. if detail <= _detail_level:
  84. if _hide_sensitive_data:
  85. args = tuple([_strip_sensitive_data_from_dict(arg) if isinstance(arg, dict) else arg for arg in args])
  86. encoded_message = (get_detail_level_name(detail) + ':' + message % args).encode(_logging_encoding, 'backslashreplace')
  87. if str is not bytes: # Python 3
  88. encoded_message = encoded_message.decode()
  89. if len(encoded_message) > _max_line_length:
  90. logger.log(_logging_level, encoded_message[:_max_line_length] + ' <removed %d remaining bytes in this log line>' % (len(encoded_message) - _max_line_length, ))
  91. else:
  92. logger.log(_logging_level, encoded_message)
  93. def log_enabled(detail):
  94. if detail <= _detail_level:
  95. if logger.isEnabledFor(_logging_level):
  96. return True
  97. return False
  98. def set_library_log_hide_sensitive_data(hide=True):
  99. global _hide_sensitive_data
  100. if hide:
  101. _hide_sensitive_data = True
  102. else:
  103. _hide_sensitive_data = False
  104. if log_enabled(ERROR):
  105. log(ERROR, 'hide sensitive data set to ' + str(_hide_sensitive_data))
  106. def get_library_log_hide_sensitive_data():
  107. return True if _hide_sensitive_data else False
  108. def set_library_log_activation_level(logging_level):
  109. if isinstance(logging_level, int):
  110. global _logging_level
  111. _logging_level = logging_level
  112. else:
  113. if log_enabled(ERROR):
  114. log(ERROR, 'invalid library log activation level <%s> ', logging_level)
  115. raise ValueError('invalid library log activation level')
  116. def get_library_log_activation_lavel():
  117. return _logging_level
  118. def set_library_log_max_line_length(length):
  119. if isinstance(length, int):
  120. global _max_line_length
  121. _max_line_length = length
  122. else:
  123. if log_enabled(ERROR):
  124. log(ERROR, 'invalid log max line length <%s> ', length)
  125. raise ValueError('invalid library log max line length')
  126. def get_library_log_max_line_length():
  127. return _max_line_length
  128. def set_library_log_detail_level(detail):
  129. if detail in DETAIL_LEVELS:
  130. global _detail_level
  131. _detail_level = detail
  132. if log_enabled(ERROR):
  133. log(ERROR, 'detail level set to ' + get_detail_level_name(_detail_level))
  134. else:
  135. if log_enabled(ERROR):
  136. log(ERROR, 'unable to set log detail level to <%s>', detail)
  137. raise ValueError('invalid library log detail level')
  138. def get_library_log_detail_level():
  139. return _detail_level
  140. def format_ldap_message(message, prefix):
  141. prefixed = ''
  142. for line in (message.prettyPrint().split('\n') if isinstance(message, LDAPMessage) else pformat(message).split('\n')): # uses pyasn1 LDAP message prettyPrint() method
  143. if line:
  144. if _hide_sensitive_data and line.strip().lower().startswith(_sensitive_lines): # _sensitive_lines is a tuple. startswith() method checks each tuple element
  145. tag, _, data = line.partition('=')
  146. if data.startswith("b'") and data.endswith("'") or data.startswith('b"') and data.endswith('"'):
  147. prefixed += '\n' + prefix + tag + '=<stripped %d characters of sensitive data>' % (len(data) - 3, )
  148. else:
  149. prefixed += '\n' + prefix + tag + '=<stripped %d characters of sensitive data>' % len(data)
  150. else:
  151. prefixed += '\n' + prefix + line
  152. return prefixed
  153. # sets a logger for the library with NullHandler. It can be used by the application with its own logging configuration
  154. logger = getLogger('ldap3')
  155. logger.addHandler(NullHandler())
  156. # sets defaults for the library logging
  157. set_library_log_activation_level(DEBUG)
  158. set_library_log_detail_level(OFF)
  159. set_library_log_hide_sensitive_data(True)