123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- """
- """
-
- # Created on 2015.05.01
- #
- # Author: Giovanni Cannata
- #
- # Copyright 2015 - 2018 Giovanni Cannata
- #
- # This file is part of ldap3.
- #
- # ldap3 is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # ldap3 is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public License
- # along with ldap3 in the COPYING and COPYING.LESSER files.
- # If not, see <http://www.gnu.org/licenses/>.
-
- from logging import getLogger, DEBUG
- from copy import deepcopy
- from pprint import pformat
- from ..protocol.rfc4511 import LDAPMessage
-
- # logging levels
- OFF = 0
- ERROR = 10
- BASIC = 20
- PROTOCOL = 30
- NETWORK = 40
- EXTENDED = 50
-
- _sensitive_lines = ('simple', 'credentials', 'serversaslcreds') # must be a tuple, not a list, lowercase
- _sensitive_args = ('simple', 'password', 'sasl_credentials', 'saslcreds', 'server_creds')
- _sensitive_attrs = ('userpassword', 'unicodepwd')
-
- _hide_sensitive_data = None
-
- DETAIL_LEVELS = [OFF, ERROR, BASIC, PROTOCOL, NETWORK, EXTENDED]
-
- _max_line_length = 4096
- _logging_level = None
- _detail_level = None
- _logging_encoding = 'ascii'
-
- try:
- from logging import NullHandler
- except ImportError: # NullHandler not present in Python < 2.7
- from logging import Handler
-
- class NullHandler(Handler):
- def handle(self, record):
- pass
-
- def emit(self, record):
- pass
-
- def createLock(self):
- self.lock = None
-
-
- def _strip_sensitive_data_from_dict(d):
- if not isinstance(d, dict):
- return d
-
- try:
- d = deepcopy(d)
- except Exception: # if deepcopy goes wrong gives up and returns the dict unchanged
- return d
- for k in d.keys():
- if isinstance(d[k], dict):
- d[k] = _strip_sensitive_data_from_dict(d[k])
- elif k.lower() in _sensitive_args and d[k]:
- d[k] = '<stripped %d characters of sensitive data>' % len(d[k])
-
- return d
-
-
- def get_detail_level_name(level_name):
- if level_name == OFF:
- return 'OFF'
- elif level_name == ERROR:
- return 'ERROR'
- elif level_name == BASIC:
- return 'BASIC'
- elif level_name == PROTOCOL:
- return 'PROTOCOL'
- elif level_name == NETWORK:
- return 'NETWORK'
- elif level_name == EXTENDED:
- return 'EXTENDED'
- raise ValueError('unknown detail level')
-
-
- def log(detail, message, *args):
- if detail <= _detail_level:
- if _hide_sensitive_data:
- args = tuple([_strip_sensitive_data_from_dict(arg) if isinstance(arg, dict) else arg for arg in args])
-
- encoded_message = (get_detail_level_name(detail) + ':' + message % args).encode(_logging_encoding, 'backslashreplace')
- if str is not bytes: # Python 3
- encoded_message = encoded_message.decode()
-
- if len(encoded_message) > _max_line_length:
- logger.log(_logging_level, encoded_message[:_max_line_length] + ' <removed %d remaining bytes in this log line>' % (len(encoded_message) - _max_line_length, ))
- else:
- logger.log(_logging_level, encoded_message)
-
-
- def log_enabled(detail):
- if detail <= _detail_level:
- if logger.isEnabledFor(_logging_level):
- return True
-
- return False
-
-
- def set_library_log_hide_sensitive_data(hide=True):
- global _hide_sensitive_data
- if hide:
- _hide_sensitive_data = True
- else:
- _hide_sensitive_data = False
- if log_enabled(ERROR):
- log(ERROR, 'hide sensitive data set to ' + str(_hide_sensitive_data))
-
-
- def get_library_log_hide_sensitive_data():
- return True if _hide_sensitive_data else False
-
-
- def set_library_log_activation_level(logging_level):
- if isinstance(logging_level, int):
- global _logging_level
- _logging_level = logging_level
- else:
- if log_enabled(ERROR):
- log(ERROR, 'invalid library log activation level <%s> ', logging_level)
- raise ValueError('invalid library log activation level')
-
-
- def get_library_log_activation_lavel():
- return _logging_level
-
-
- def set_library_log_max_line_length(length):
- if isinstance(length, int):
- global _max_line_length
- _max_line_length = length
- else:
- if log_enabled(ERROR):
- log(ERROR, 'invalid log max line length <%s> ', length)
- raise ValueError('invalid library log max line length')
-
-
- def get_library_log_max_line_length():
- return _max_line_length
-
-
- def set_library_log_detail_level(detail):
- if detail in DETAIL_LEVELS:
- global _detail_level
- _detail_level = detail
- if log_enabled(ERROR):
- log(ERROR, 'detail level set to ' + get_detail_level_name(_detail_level))
- else:
- if log_enabled(ERROR):
- log(ERROR, 'unable to set log detail level to <%s>', detail)
- raise ValueError('invalid library log detail level')
-
-
- def get_library_log_detail_level():
- return _detail_level
-
-
- def format_ldap_message(message, prefix):
- prefixed = ''
- for line in (message.prettyPrint().split('\n') if isinstance(message, LDAPMessage) else pformat(message).split('\n')): # uses pyasn1 LDAP message prettyPrint() method
- if line:
- if _hide_sensitive_data and line.strip().lower().startswith(_sensitive_lines): # _sensitive_lines is a tuple. startswith() method checks each tuple element
- tag, _, data = line.partition('=')
- if data.startswith("b'") and data.endswith("'") or data.startswith('b"') and data.endswith('"'):
- prefixed += '\n' + prefix + tag + '=<stripped %d characters of sensitive data>' % (len(data) - 3, )
- else:
- prefixed += '\n' + prefix + tag + '=<stripped %d characters of sensitive data>' % len(data)
- else:
- prefixed += '\n' + prefix + line
- return prefixed
-
- # sets a logger for the library with NullHandler. It can be used by the application with its own logging configuration
- logger = getLogger('ldap3')
- logger.addHandler(NullHandler())
-
- # sets defaults for the library logging
- set_library_log_activation_level(DEBUG)
- set_library_log_detail_level(OFF)
- set_library_log_hide_sensitive_data(True)
|