codekingpro/portable-devtools
114k
1"""2"""3 4# Created on 2013.07.245#6# Author: Giovanni Cannata7#8# Copyright 2013 - 2020 Giovanni Cannata9#10# This file is part of ldap3.11#12# ldap3 is free software: you can redistribute it and/or modify13# it under the terms of the GNU Lesser General Public License as published14# by the Free Software Foundation, either version 3 of the License, or15# (at your option) any later version.16#17# ldap3 is distributed in the hope that it will be useful,18# but WITHOUT ANY WARRANTY; without even the implied warranty of19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the20# GNU Lesser General Public License for more details.21#22# You should have received a copy of the GNU Lesser General Public License23# along with ldap3 in the COPYING and COPYING.LESSER files.24# If not, see <http://www.gnu.org/licenses/>.25from pyasn1.error import PyAsn1Error26 27from .. import SEQUENCE_TYPES, STRING_TYPES, get_config_parameter28from ..core.exceptions import LDAPControlError, LDAPAttributeError, LDAPObjectClassError, LDAPInvalidValueError29from ..protocol.rfc4511 import Controls, Control30from ..utils.conv import to_raw, to_unicode, escape_filter_chars, is_filter_escaped31from ..protocol.formatters.standard import find_attribute_validator32 33 34def to_str_or_normalized_unicode(val):35 """ Attempt to convert value to a string. If that would error, convert it to normalized unicode.36 Python 3 string conversion handles unicode -> str without issue, but python 2 doesn't.37 """38 try:39 return str(val)40 except:41 return val.encode('ascii', 'backslashreplace')42 43 44def attribute_to_dict(attribute):45 try:46 return {'type': str(attribute['type']), 'values': [str(val) for val in attribute['vals']]}47 except PyAsn1Error: # invalid encoding, return bytes value48 return {'type': str(attribute['type']), 'values': [bytes(val) for val in attribute['vals']]}49 50 51def attributes_to_dict(attributes):52 attributes_dict = dict()53 for attribute in attributes:54 attribute_dict = attribute_to_dict(attribute)55 attributes_dict[attribute_dict['type']] = attribute_dict['values']56 return attributes_dict57 58 59def referrals_to_list(referrals):60 if isinstance(referrals, list):61 return [to_str_or_normalized_unicode(referral) for referral in referrals if referral] if referrals else None62 else:63 return [to_str_or_normalized_unicode(referral) for referral in referrals if referral] if referrals is not None and referrals.hasValue() else None64 65 66def search_refs_to_list(search_refs):67 return [to_str_or_normalized_unicode(search_ref) for search_ref in search_refs if search_ref] if search_refs else None68 69 70def search_refs_to_list_fast(search_refs):71 return [to_unicode(search_ref) for search_ref in search_refs if search_ref] if search_refs else None72 73 74def sasl_to_dict(sasl):75 return {'mechanism': str(sasl['mechanism']), 'credentials': bytes(sasl['credentials']) if sasl['credentials'] is not None and sasl['credentials'].hasValue() else None}76 77 78def authentication_choice_to_dict(authentication_choice):79 return {'simple': str(authentication_choice['simple']) if authentication_choice.getName() == 'simple' else None, 'sasl': sasl_to_dict(authentication_choice['sasl']) if authentication_choice.getName() == 'sasl' else None}80 81 82def partial_attribute_to_dict(modification):83 try:84 return {'type': str(modification['type']), 'value': [str(value) for value in modification['vals']]}85 except PyAsn1Error: # invalid encoding, return bytes value86 return {'type': str(modification['type']), 'value': [bytes(value) for value in modification['vals']]}87 88 89def change_to_dict(change):90 return {'operation': int(change['operation']), 'attribute': partial_attribute_to_dict(change['modification'])}91 92 93def changes_to_list(changes):94 return [change_to_dict(change) for change in changes]95 96 97def attributes_to_list(attributes):98 return [to_str_or_normalized_unicode(attribute) for attribute in attributes]99 100 101def ava_to_dict(ava):102 try:103 return {'attribute': str(ava['attributeDesc']), 'value': escape_filter_chars(str(ava['assertionValue']))}104 except Exception: # invalid encoding, return bytes value105 try:106 return {'attribute': str(ava['attributeDesc']), 'value': escape_filter_chars(bytes(ava['assertionValue']))}107 except Exception:108 return {'attribute': str(ava['attributeDesc']), 'value': bytes(ava['assertionValue'])}109 110 111def substring_to_dict(substring):112 return {'initial': substring['initial'] if substring['initial'] else '', 'any': [middle for middle in substring['any']] if substring['any'] else '', 'final': substring['final'] if substring['final'] else ''}113 114 115def prepare_changes_for_request(changes):116 prepared = dict()117 for change in changes:118 attribute_name = change['attribute']['type']119 if attribute_name not in prepared:120 prepared[attribute_name] = []121 prepared[attribute_name].append((change['operation'], change['attribute']['value']))122 return prepared123 124 125def build_controls_list(controls):126 """controls is a sequence of Control() or sequences127 each sequence must have 3 elements: the control OID, the criticality, the value128 criticality must be a boolean129 """130 131 if not controls:132 return None133 134 if not isinstance(controls, SEQUENCE_TYPES):135 raise LDAPControlError('controls must be a sequence')136 137 built_controls = Controls()138 for idx, control in enumerate(controls):139 if isinstance(control, Control):140 built_controls.setComponentByPosition(idx, control)141 elif len(control) == 3 and isinstance(control[1], bool):142 built_control = Control()143 built_control['controlType'] = control[0]144 built_control['criticality'] = control[1]145 if control[2] is not None:146 built_control['controlValue'] = control[2]147 built_controls.setComponentByPosition(idx, built_control)148 else:149 raise LDAPControlError('control must be a sequence of 3 elements: controlType, criticality (boolean) and controlValue (None if not provided)')150 151 return built_controls152 153 154def validate_assertion_value(schema, name, value, auto_escape, auto_encode, validator, check_names):155 value = to_unicode(value)156 if auto_escape:157 if '\\' in value and not is_filter_escaped(value):158 value = escape_filter_chars(value)159 value = validate_attribute_value(schema, name, value, auto_encode, validator=validator, check_names=check_names)160 return value161 162 163def validate_attribute_value(schema, name, value, auto_encode, validator=None, check_names=False):164 conf_classes_excluded_from_check = [v.lower() for v in get_config_parameter('CLASSES_EXCLUDED_FROM_CHECK')]165 conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')]166 conf_utf8_syntaxes = get_config_parameter('UTF8_ENCODED_SYNTAXES')167 conf_utf8_types = [v.lower() for v in get_config_parameter('UTF8_ENCODED_TYPES')]168 if schema and schema.attribute_types:169 if ';' in name:170 name = name.split(';')[0]171 if check_names and schema.object_classes and name.lower() == 'objectclass':172 if to_unicode(value).lower() not in conf_classes_excluded_from_check and to_unicode(value) not in schema.object_classes:173 raise LDAPObjectClassError('invalid class in objectClass attribute: ' + str(value))174 elif check_names and name not in schema.attribute_types and name.lower() not in conf_attributes_excluded_from_check:175 raise LDAPAttributeError('invalid attribute ' + name)176 else: # try standard validators177 validator = find_attribute_validator(schema, name, validator)178 validated = validator(value)179 if validated is False:180 try: # checks if the value is a byte value erroneously converted to a string (as "b'1234'"), this is a common case in Python 3 when encoding is not specified181 if value[0:2] == "b'" and value [-1] == "'":182 value = to_raw(value[2:-1])183 validated = validator(value)184 except Exception:185 raise LDAPInvalidValueError('value \'%s\' non valid for attribute \'%s\'' % (value, name))186 if validated is False:187 raise LDAPInvalidValueError('value \'%s\' non valid for attribute \'%s\'' % (value, name))188 elif validated is not True: # a valid LDAP value equivalent to the actual value189 value = validated190 # converts to utf-8 for well known Unicode LDAP syntaxes191 if auto_encode and ((name in schema.attribute_types and schema.attribute_types[name].syntax in conf_utf8_syntaxes) or name.lower() in conf_utf8_types):192 value = to_unicode(value) # tries to convert from local encoding to Unicode193 return to_raw(value)194 195 196def prepare_filter_for_sending(raw_string):197 i = 0198 ints = []199 raw_string = to_raw(raw_string)200 while i < len(raw_string):201 if (raw_string[i] == 92 or raw_string[i] == '\\') and i < len(raw_string) - 2: # 92 (0x5C) is backslash202 try:203 ints.append(int(raw_string[i + 1: i + 3], 16))204 i += 2205 except ValueError: # not an ldap escaped value, sends as is206 ints.append(92) # adds backslash207 else:208 if str is not bytes: # Python 3209 ints.append(raw_string[i])210 else: # Python 2211 ints.append(ord(raw_string[i]))212 i += 1213 214 if str is not bytes: # Python 3215 return bytes(ints)216 else: # Python 2217 return ''.join(chr(x) for x in ints)218 219 220def prepare_for_sending(raw_string):221 return to_raw(raw_string) if isinstance(raw_string, STRING_TYPES) else raw_string222 