2025-12-25 upload
This commit is contained in:
216
venv/Lib/site-packages/ldap3/utils/log.py
Normal file
216
venv/Lib/site-packages/ldap3/utils/log.py
Normal file
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
"""
|
||||
|
||||
# Created on 2015.05.01
|
||||
#
|
||||
# Author: Giovanni Cannata
|
||||
#
|
||||
# Copyright 2015 - 2020 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 = 0
|
||||
_detail_level = 0
|
||||
_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])
|
||||
|
||||
if str is not bytes: # Python 3
|
||||
encoded_message = (get_detail_level_name(detail) + ':' + message % args).encode(_logging_encoding, 'backslashreplace')
|
||||
encoded_message = encoded_message.decode()
|
||||
else:
|
||||
try:
|
||||
encoded_message = (get_detail_level_name(detail) + ':' + message % args).encode(_logging_encoding, 'replace')
|
||||
except Exception:
|
||||
encoded_message = (get_detail_level_name(detail) + ':' + message % args).decode(_logging_encoding, 'replace')
|
||||
|
||||
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):
|
||||
if isinstance(message, LDAPMessage):
|
||||
try: # pyasn1 prettyprint raises exception in version 0.4.3
|
||||
formatted = message.prettyPrint().split('\n') # pyasn1 pretty print
|
||||
except Exception as e:
|
||||
formatted = ['pyasn1 exception', str(e)]
|
||||
else:
|
||||
formatted = pformat(message).split('\n')
|
||||
|
||||
prefixed = ''
|
||||
for line in formatted:
|
||||
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)
|
||||
Reference in New Issue
Block a user