選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 

222 行
9.6 KiB

  1. """
  2. """
  3. # Created on 2013.07.24
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2013 - 2020 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 pyasn1.error import PyAsn1Error
  25. from .. import SEQUENCE_TYPES, STRING_TYPES, get_config_parameter
  26. from ..core.exceptions import LDAPControlError, LDAPAttributeError, LDAPObjectClassError, LDAPInvalidValueError
  27. from ..protocol.rfc4511 import Controls, Control
  28. from ..utils.conv import to_raw, to_unicode, escape_filter_chars, is_filter_escaped
  29. from ..protocol.formatters.standard import find_attribute_validator
  30. def to_str_or_normalized_unicode(val):
  31. """ Attempt to convert value to a string. If that would error, convert it to normalized unicode.
  32. Python 3 string conversion handles unicode -> str without issue, but python 2 doesn't.
  33. """
  34. try:
  35. return str(val)
  36. except:
  37. return val.encode('ascii', 'backslashreplace')
  38. def attribute_to_dict(attribute):
  39. try:
  40. return {'type': str(attribute['type']), 'values': [str(val) for val in attribute['vals']]}
  41. except PyAsn1Error: # invalid encoding, return bytes value
  42. return {'type': str(attribute['type']), 'values': [bytes(val) for val in attribute['vals']]}
  43. def attributes_to_dict(attributes):
  44. attributes_dict = dict()
  45. for attribute in attributes:
  46. attribute_dict = attribute_to_dict(attribute)
  47. attributes_dict[attribute_dict['type']] = attribute_dict['values']
  48. return attributes_dict
  49. def referrals_to_list(referrals):
  50. if isinstance(referrals, list):
  51. return [to_str_or_normalized_unicode(referral) for referral in referrals if referral] if referrals else None
  52. else:
  53. return [to_str_or_normalized_unicode(referral) for referral in referrals if referral] if referrals is not None and referrals.hasValue() else None
  54. def search_refs_to_list(search_refs):
  55. return [to_str_or_normalized_unicode(search_ref) for search_ref in search_refs if search_ref] if search_refs else None
  56. def search_refs_to_list_fast(search_refs):
  57. return [to_unicode(search_ref) for search_ref in search_refs if search_ref] if search_refs else None
  58. def sasl_to_dict(sasl):
  59. return {'mechanism': str(sasl['mechanism']), 'credentials': bytes(sasl['credentials']) if sasl['credentials'] is not None and sasl['credentials'].hasValue() else None}
  60. def authentication_choice_to_dict(authentication_choice):
  61. 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}
  62. def partial_attribute_to_dict(modification):
  63. try:
  64. return {'type': str(modification['type']), 'value': [str(value) for value in modification['vals']]}
  65. except PyAsn1Error: # invalid encoding, return bytes value
  66. return {'type': str(modification['type']), 'value': [bytes(value) for value in modification['vals']]}
  67. def change_to_dict(change):
  68. return {'operation': int(change['operation']), 'attribute': partial_attribute_to_dict(change['modification'])}
  69. def changes_to_list(changes):
  70. return [change_to_dict(change) for change in changes]
  71. def attributes_to_list(attributes):
  72. return [to_str_or_normalized_unicode(attribute) for attribute in attributes]
  73. def ava_to_dict(ava):
  74. try:
  75. return {'attribute': str(ava['attributeDesc']), 'value': escape_filter_chars(str(ava['assertionValue']))}
  76. except Exception: # invalid encoding, return bytes value
  77. try:
  78. return {'attribute': str(ava['attributeDesc']), 'value': escape_filter_chars(bytes(ava['assertionValue']))}
  79. except Exception:
  80. return {'attribute': str(ava['attributeDesc']), 'value': bytes(ava['assertionValue'])}
  81. def substring_to_dict(substring):
  82. 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 ''}
  83. def prepare_changes_for_request(changes):
  84. prepared = dict()
  85. for change in changes:
  86. attribute_name = change['attribute']['type']
  87. if attribute_name not in prepared:
  88. prepared[attribute_name] = []
  89. prepared[attribute_name].append((change['operation'], change['attribute']['value']))
  90. return prepared
  91. def build_controls_list(controls):
  92. """controls is a sequence of Control() or sequences
  93. each sequence must have 3 elements: the control OID, the criticality, the value
  94. criticality must be a boolean
  95. """
  96. if not controls:
  97. return None
  98. if not isinstance(controls, SEQUENCE_TYPES):
  99. raise LDAPControlError('controls must be a sequence')
  100. built_controls = Controls()
  101. for idx, control in enumerate(controls):
  102. if isinstance(control, Control):
  103. built_controls.setComponentByPosition(idx, control)
  104. elif len(control) == 3 and isinstance(control[1], bool):
  105. built_control = Control()
  106. built_control['controlType'] = control[0]
  107. built_control['criticality'] = control[1]
  108. if control[2] is not None:
  109. built_control['controlValue'] = control[2]
  110. built_controls.setComponentByPosition(idx, built_control)
  111. else:
  112. raise LDAPControlError('control must be a sequence of 3 elements: controlType, criticality (boolean) and controlValue (None if not provided)')
  113. return built_controls
  114. def validate_assertion_value(schema, name, value, auto_escape, auto_encode, validator, check_names):
  115. value = to_unicode(value)
  116. if auto_escape:
  117. if '\\' in value and not is_filter_escaped(value):
  118. value = escape_filter_chars(value)
  119. value = validate_attribute_value(schema, name, value, auto_encode, validator=validator, check_names=check_names)
  120. return value
  121. def validate_attribute_value(schema, name, value, auto_encode, validator=None, check_names=False):
  122. conf_classes_excluded_from_check = [v.lower() for v in get_config_parameter('CLASSES_EXCLUDED_FROM_CHECK')]
  123. conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')]
  124. conf_utf8_syntaxes = get_config_parameter('UTF8_ENCODED_SYNTAXES')
  125. conf_utf8_types = [v.lower() for v in get_config_parameter('UTF8_ENCODED_TYPES')]
  126. if schema and schema.attribute_types:
  127. if ';' in name:
  128. name = name.split(';')[0]
  129. if check_names and schema.object_classes and name.lower() == 'objectclass':
  130. if to_unicode(value).lower() not in conf_classes_excluded_from_check and to_unicode(value) not in schema.object_classes:
  131. raise LDAPObjectClassError('invalid class in objectClass attribute: ' + str(value))
  132. elif check_names and name not in schema.attribute_types and name.lower() not in conf_attributes_excluded_from_check:
  133. raise LDAPAttributeError('invalid attribute ' + name)
  134. else: # try standard validators
  135. validator = find_attribute_validator(schema, name, validator)
  136. validated = validator(value)
  137. if validated is False:
  138. 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
  139. if value[0:2] == "b'" and value [-1] == "'":
  140. value = to_raw(value[2:-1])
  141. validated = validator(value)
  142. except Exception:
  143. raise LDAPInvalidValueError('value \'%s\' non valid for attribute \'%s\'' % (value, name))
  144. if validated is False:
  145. raise LDAPInvalidValueError('value \'%s\' non valid for attribute \'%s\'' % (value, name))
  146. elif validated is not True: # a valid LDAP value equivalent to the actual value
  147. value = validated
  148. # converts to utf-8 for well known Unicode LDAP syntaxes
  149. 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):
  150. value = to_unicode(value) # tries to convert from local encoding to Unicode
  151. return to_raw(value)
  152. def prepare_filter_for_sending(raw_string):
  153. i = 0
  154. ints = []
  155. raw_string = to_raw(raw_string)
  156. while i < len(raw_string):
  157. if (raw_string[i] == 92 or raw_string[i] == '\\') and i < len(raw_string) - 2: # 92 (0x5C) is backslash
  158. try:
  159. ints.append(int(raw_string[i + 1: i + 3], 16))
  160. i += 2
  161. except ValueError: # not an ldap escaped value, sends as is
  162. ints.append(92) # adds backslash
  163. else:
  164. if str is not bytes: # Python 3
  165. ints.append(raw_string[i])
  166. else: # Python 2
  167. ints.append(ord(raw_string[i]))
  168. i += 1
  169. if str is not bytes: # Python 3
  170. return bytes(ints)
  171. else: # Python 2
  172. return ''.join(chr(x) for x in ints)
  173. def prepare_for_sending(raw_string):
  174. return to_raw(raw_string) if isinstance(raw_string, STRING_TYPES) else raw_string