1""" 2""" 3 4# Created on 2013.07.24 5# 6# Author: Giovanni Cannata 7# 8# Copyright 2013 - 2020 Giovanni Cannata 9# 10# This file is part of ldap3. 11# 12# ldap3 is free software: you can redistribute it and/or modify 13# it under the terms of the GNU Lesser General Public License as published 14# by the Free Software Foundation, either version 3 of the License, or 15# (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 of 19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20# GNU Lesser General Public License for more details. 21# 22# You should have received a copy of the GNU Lesser General Public License 23# along with ldap3 in the COPYING and COPYING.LESSER files. 24# If not, see <http://www.gnu.org/licenses/>. 25from pyasn1.error import PyAsn1Error 26 27from .. import SEQUENCE_TYPES, STRING_TYPES, get_config_parameter 28from ..core.exceptions import LDAPControlError, LDAPAttributeError, LDAPObjectClassError, LDAPInvalidValueError 29from ..protocol.rfc4511 import Controls, Control 30from ..utils.conv import to_raw, to_unicode, escape_filter_chars, is_filter_escaped 31from ..protocol.formatters.standard import find_attribute_validator 32 33 34def attribute_to_dict(attribute): 35 try: 36 return {'type': str(attribute['type']), 'values': [str(val) for val in attribute['vals']]} 37 except PyAsn1Error: # invalid encoding, return bytes value 38 return {'type': str(attribute['type']), 'values': [bytes(val) for val in attribute['vals']]} 39 40 41def attributes_to_dict(attributes): 42 attributes_dict = dict() 43 for attribute in attributes: 44 attribute_dict = attribute_to_dict(attribute) 45 attributes_dict[attribute_dict['type']] = attribute_dict['values'] 46 return attributes_dict 47 48 49def referrals_to_list(referrals): 50 if isinstance(referrals, list): 51 return [str(referral) for referral in referrals if referral] if referrals else None 52 else: 53 return [str(referral) for referral in referrals if referral] if referrals is not None and referrals.hasValue() else None 54 55 56def search_refs_to_list(search_refs): 57 return [str(search_ref) for search_ref in search_refs if search_ref] if search_refs else None 58 59 60def search_refs_to_list_fast(search_refs): 61 return [to_unicode(search_ref) for search_ref in search_refs if search_ref] if search_refs else None 62 63 64def sasl_to_dict(sasl): 65 return {'mechanism': str(sasl['mechanism']), 'credentials': bytes(sasl['credentials']) if sasl['credentials'] is not None and sasl['credentials'].hasValue() else None} 66 67 68def authentication_choice_to_dict(authentication_choice): 69 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} 70 71 72def partial_attribute_to_dict(modification): 73 try: 74 return {'type': str(modification['type']), 'value': [str(value) for value in modification['vals']]} 75 except PyAsn1Error: # invalid encoding, return bytes value 76 return {'type': str(modification['type']), 'value': [bytes(value) for value in modification['vals']]} 77 78 79def change_to_dict(change): 80 return {'operation': int(change['operation']), 'attribute': partial_attribute_to_dict(change['modification'])} 81 82 83def changes_to_list(changes): 84 return [change_to_dict(change) for change in changes] 85 86 87def attributes_to_list(attributes): 88 return [str(attribute) for attribute in attributes] 89 90 91def ava_to_dict(ava): 92 try: 93 return {'attribute': str(ava['attributeDesc']), 'value': escape_filter_chars(str(ava['assertionValue']))} 94 except Exception: # invalid encoding, return bytes value 95 try: 96 return {'attribute': str(ava['attributeDesc']), 'value': escape_filter_chars(bytes(ava['assertionValue']))} 97 except Exception: 98 return {'attribute': str(ava['attributeDesc']), 'value': bytes(ava['assertionValue'])} 99 100 101def substring_to_dict(substring): 102 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 ''} 103 104 105def prepare_changes_for_request(changes): 106 prepared = dict() 107 for change in changes: 108 attribute_name = change['attribute']['type'] 109 if attribute_name not in prepared: 110 prepared[attribute_name] = [] 111 prepared[attribute_name].append((change['operation'], change['attribute']['value'])) 112 return prepared 113 114 115def build_controls_list(controls): 116 """controls is a sequence of Control() or sequences 117 each sequence must have 3 elements: the control OID, the criticality, the value 118 criticality must be a boolean 119 """ 120 121 if not controls: 122 return None 123 124 if not isinstance(controls, SEQUENCE_TYPES): 125 raise LDAPControlError('controls must be a sequence') 126 127 built_controls = Controls() 128 for idx, control in enumerate(controls): 129 if isinstance(control, Control): 130 built_controls.setComponentByPosition(idx, control) 131 elif len(control) == 3 and isinstance(control[1], bool): 132 built_control = Control() 133 built_control['controlType'] = control[0] 134 built_control['criticality'] = control[1] 135 if control[2] is not None: 136 built_control['controlValue'] = control[2] 137 built_controls.setComponentByPosition(idx, built_control) 138 else: 139 raise LDAPControlError('control must be a sequence of 3 elements: controlType, criticality (boolean) and controlValue (None if not provided)') 140 141 return built_controls 142 143 144def validate_assertion_value(schema, name, value, auto_escape, auto_encode, validator, check_names): 145 value = to_unicode(value) 146 if auto_escape: 147 if '\\' in value and not is_filter_escaped(value): 148 value = escape_filter_chars(value) 149 value = validate_attribute_value(schema, name, value, auto_encode, validator=validator, check_names=check_names) 150 return value 151 152 153def validate_attribute_value(schema, name, value, auto_encode, validator=None, check_names=False): 154 conf_classes_excluded_from_check = [v.lower() for v in get_config_parameter('CLASSES_EXCLUDED_FROM_CHECK')] 155 conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')] 156 conf_utf8_syntaxes = get_config_parameter('UTF8_ENCODED_SYNTAXES') 157 conf_utf8_types = [v.lower() for v in get_config_parameter('UTF8_ENCODED_TYPES')] 158 if schema and schema.attribute_types: 159 if ';' in name: 160 name = name.split(';')[0] 161 if check_names and schema.object_classes and name.lower() == 'objectclass': 162 if to_unicode(value).lower() not in conf_classes_excluded_from_check and to_unicode(value) not in schema.object_classes: 163 raise LDAPObjectClassError('invalid class in objectClass attribute: ' + str(value)) 164 elif check_names and name not in schema.attribute_types and name.lower() not in conf_attributes_excluded_from_check: 165 raise LDAPAttributeError('invalid attribute ' + name) 166 else: # try standard validators 167 validator = find_attribute_validator(schema, name, validator) 168 validated = validator(value) 169 if validated is False: 170 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 specified 171 if value[0:2] == "b'" and value [-1] == "'": 172 value = to_raw(value[2:-1]) 173 validated = validator(value) 174 except Exception: 175 raise LDAPInvalidValueError('value \'%s\' non valid for attribute \'%s\'' % (value, name)) 176 if validated is False: 177 raise LDAPInvalidValueError('value \'%s\' non valid for attribute \'%s\'' % (value, name)) 178 elif validated is not True: # a valid LDAP value equivalent to the actual value 179 value = validated 180 # converts to utf-8 for well known Unicode LDAP syntaxes 181 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): 182 value = to_unicode(value) # tries to convert from local encoding to Unicode 183 return to_raw(value) 184 185 186def prepare_filter_for_sending(raw_string): 187 i = 0 188 ints = [] 189 raw_string = to_raw(raw_string) 190 while i < len(raw_string): 191 if (raw_string[i] == 92 or raw_string[i] == '\\') and i < len(raw_string) - 2: # 92 (0x5C) is backslash 192 try: 193 ints.append(int(raw_string[i + 1: i + 3], 16)) 194 i += 2 195 except ValueError: # not an ldap escaped value, sends as is 196 ints.append(92) # adds backslash 197 else: 198 if str is not bytes: # Python 3 199 ints.append(raw_string[i]) 200 else: # Python 2 201 ints.append(ord(raw_string[i])) 202 i += 1 203 204 if str is not bytes: # Python 3 205 return bytes(ints) 206 else: # Python 2 207 return ''.join(chr(x) for x in ints) 208 209 210def prepare_for_sending(raw_string): 211 return to_raw(raw_string) if isinstance(raw_string, STRING_TYPES) else raw_string 212