Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

search.py 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. """
  2. """
  3. # Created on 2013.06.02
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2013 - 2018 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 string import whitespace
  25. from os import linesep
  26. from .. import DEREF_NEVER, BASE, LEVEL, SUBTREE, DEREF_SEARCH, DEREF_BASE, DEREF_ALWAYS, NO_ATTRIBUTES, SEQUENCE_TYPES, get_config_parameter, STRING_TYPES
  27. from ..core.exceptions import LDAPInvalidFilterError, LDAPAttributeError, LDAPInvalidScopeError, LDAPInvalidDereferenceAliasesError
  28. from ..utils.ciDict import CaseInsensitiveDict
  29. from ..protocol.rfc4511 import SearchRequest, LDAPDN, Scope, DerefAliases, Integer0ToMax, TypesOnly, \
  30. AttributeSelection, Selector, EqualityMatch, AttributeDescription, AssertionValue, Filter, \
  31. Not, And, Or, ApproxMatch, GreaterOrEqual, LessOrEqual, ExtensibleMatch, Present, SubstringFilter, \
  32. Substrings, Final, Initial, Any, ResultCode, Substring, MatchingRule, Type, MatchValue, DnAttributes
  33. from ..operation.bind import referrals_to_list
  34. from ..protocol.convert import ava_to_dict, attributes_to_list, search_refs_to_list, validate_assertion_value, prepare_filter_for_sending, search_refs_to_list_fast
  35. from ..protocol.formatters.standard import format_attribute_values
  36. from ..utils.conv import to_unicode, to_raw
  37. ROOT = 0
  38. AND = 1
  39. OR = 2
  40. NOT = 3
  41. MATCH_APPROX = 4
  42. MATCH_GREATER_OR_EQUAL = 5
  43. MATCH_LESS_OR_EQUAL = 6
  44. MATCH_EXTENSIBLE = 7
  45. MATCH_PRESENT = 8
  46. MATCH_SUBSTRING = 9
  47. MATCH_EQUAL = 10
  48. SEARCH_OPEN = 20
  49. SEARCH_OPEN_OR_CLOSE = 21
  50. SEARCH_MATCH_OR_CLOSE = 22
  51. SEARCH_MATCH_OR_CONTROL = 23
  52. class FilterNode(object):
  53. def __init__(self, tag=None, assertion=None):
  54. self.tag = tag
  55. self.parent = None
  56. self.assertion = assertion
  57. self.elements = []
  58. def append(self, filter_node):
  59. filter_node.parent = self
  60. self.elements.append(filter_node)
  61. return filter_node
  62. def __str__(self, pos=0):
  63. self.__repr__(pos)
  64. def __repr__(self, pos=0):
  65. node_tags = ['ROOT', 'AND', 'OR', 'NOT', 'MATCH_APPROX', 'MATCH_GREATER_OR_EQUAL', 'MATCH_LESS_OR_EQUAL', 'MATCH_EXTENSIBLE', 'MATCH_PRESENT', 'MATCH_SUBSTRING', 'MATCH_EQUAL']
  66. representation = ' ' * pos + 'tag: ' + node_tags[self.tag] + ' - assertion: ' + str(self.assertion)
  67. if self.elements:
  68. representation += ' - elements: ' + str(len(self.elements))
  69. for element in self.elements:
  70. representation += linesep + ' ' * pos + element.__repr__(pos + 2)
  71. return representation
  72. def evaluate_match(match, schema, auto_escape, auto_encode, validator, check_names):
  73. left_part, equal_sign, right_part = match.strip().partition('=')
  74. if not equal_sign:
  75. raise LDAPInvalidFilterError('invalid matching assertion')
  76. if left_part.endswith('~'): # approximate match '~='
  77. tag = MATCH_APPROX
  78. left_part = left_part[:-1].strip()
  79. right_part = right_part.strip()
  80. assertion = {'attr': left_part, 'value': validate_assertion_value(schema, left_part, right_part, auto_escape, auto_encode, validator, check_names)}
  81. elif left_part.endswith('>'): # greater or equal match '>='
  82. tag = MATCH_GREATER_OR_EQUAL
  83. left_part = left_part[:-1].strip()
  84. right_part = right_part.strip()
  85. assertion = {'attr': left_part, 'value': validate_assertion_value(schema, left_part, right_part, auto_escape, auto_encode, validator, check_names)}
  86. elif left_part.endswith('<'): # less or equal match '<='
  87. tag = MATCH_LESS_OR_EQUAL
  88. left_part = left_part[:-1].strip()
  89. right_part = right_part.strip()
  90. assertion = {'attr': left_part, 'value': validate_assertion_value(schema, left_part, right_part, auto_escape, auto_encode, validator, check_names)}
  91. elif left_part.endswith(':'): # extensible match ':='
  92. tag = MATCH_EXTENSIBLE
  93. left_part = left_part[:-1].strip()
  94. right_part = right_part.strip()
  95. extended_filter_list = left_part.split(':')
  96. matching_rule = False
  97. dn_attributes = False
  98. attribute_name = False
  99. if extended_filter_list[0] == '': # extensible filter format [:dn]:matchingRule:=assertionValue
  100. if len(extended_filter_list) == 2 and extended_filter_list[1].lower().strip() != 'dn':
  101. matching_rule = extended_filter_list[1]
  102. elif len(extended_filter_list) == 3 and extended_filter_list[1].lower().strip() == 'dn':
  103. dn_attributes = True
  104. matching_rule = extended_filter_list[2]
  105. else:
  106. raise LDAPInvalidFilterError('invalid extensible filter')
  107. elif len(extended_filter_list) <= 3: # extensible filter format attr[:dn][:matchingRule]:=assertionValue
  108. if len(extended_filter_list) == 1:
  109. attribute_name = extended_filter_list[0]
  110. elif len(extended_filter_list) == 2:
  111. attribute_name = extended_filter_list[0]
  112. if extended_filter_list[1].lower().strip() == 'dn':
  113. dn_attributes = True
  114. else:
  115. matching_rule = extended_filter_list[1]
  116. elif len(extended_filter_list) == 3 and extended_filter_list[1].lower().strip() == 'dn':
  117. attribute_name = extended_filter_list[0]
  118. dn_attributes = True
  119. matching_rule = extended_filter_list[2]
  120. else:
  121. raise LDAPInvalidFilterError('invalid extensible filter')
  122. if not attribute_name and not matching_rule:
  123. raise LDAPInvalidFilterError('invalid extensible filter')
  124. attribute_name = attribute_name.strip() if attribute_name else False
  125. matching_rule = matching_rule.strip() if matching_rule else False
  126. assertion = {'attr': attribute_name, 'value': validate_assertion_value(schema, attribute_name, right_part, auto_escape, auto_encode, validator, check_names), 'matchingRule': matching_rule, 'dnAttributes': dn_attributes}
  127. elif right_part == '*': # attribute present match '=*'
  128. tag = MATCH_PRESENT
  129. left_part = left_part.strip()
  130. assertion = {'attr': left_part}
  131. elif '*' in right_part: # substring match '=initial*substring*substring*final'
  132. tag = MATCH_SUBSTRING
  133. left_part = left_part.strip()
  134. right_part = right_part.strip()
  135. substrings = right_part.split('*')
  136. initial = validate_assertion_value(schema, left_part, substrings[0], auto_escape, auto_encode, validator, check_names) if substrings[0] else None
  137. final = validate_assertion_value(schema, left_part, substrings[-1], auto_escape, auto_encode, validator, check_names) if substrings[-1] else None
  138. any_string = [validate_assertion_value(schema, left_part, substring, auto_escape, auto_encode, validator, check_names) for substring in substrings[1:-1] if substring]
  139. #assertion = {'attr': left_part, 'initial': initial, 'any': any_string, 'final': final}
  140. assertion = {'attr': left_part}
  141. if initial:
  142. assertion['initial'] = initial
  143. if any_string:
  144. assertion['any'] = any_string
  145. if final:
  146. assertion['final'] = final
  147. else: # equality match '='
  148. tag = MATCH_EQUAL
  149. left_part = left_part.strip()
  150. right_part = right_part.strip()
  151. assertion = {'attr': left_part, 'value': validate_assertion_value(schema, left_part, right_part, auto_escape, auto_encode, validator, check_names)}
  152. return FilterNode(tag, assertion)
  153. def parse_filter(search_filter, schema, auto_escape, auto_encode, validator, check_names):
  154. if str != bytes and isinstance(search_filter, bytes): # python 3 with byte filter
  155. search_filter = to_unicode(search_filter)
  156. search_filter = search_filter.strip()
  157. if search_filter and search_filter.count('(') == search_filter.count(')') and search_filter.startswith('(') and search_filter.endswith(')'):
  158. state = SEARCH_OPEN_OR_CLOSE
  159. root = FilterNode(ROOT)
  160. current_node = root
  161. start_pos = None
  162. skip_white_space = True
  163. just_closed = False
  164. for pos, c in enumerate(search_filter):
  165. if skip_white_space and c in whitespace:
  166. continue
  167. elif (state == SEARCH_OPEN or state == SEARCH_OPEN_OR_CLOSE) and c == '(':
  168. state = SEARCH_MATCH_OR_CONTROL
  169. just_closed = False
  170. elif state == SEARCH_MATCH_OR_CONTROL and c in '&!|':
  171. if c == '&':
  172. current_node = current_node.append(FilterNode(AND))
  173. elif c == '|':
  174. current_node = current_node.append(FilterNode(OR))
  175. elif c == '!':
  176. current_node = current_node.append(FilterNode(NOT))
  177. state = SEARCH_OPEN
  178. elif (state == SEARCH_MATCH_OR_CLOSE or state == SEARCH_OPEN_OR_CLOSE) and c == ')':
  179. if just_closed:
  180. current_node = current_node.parent
  181. else:
  182. just_closed = True
  183. skip_white_space = True
  184. end_pos = pos
  185. if start_pos:
  186. if current_node.tag == NOT and len(current_node.elements) > 0:
  187. raise LDAPInvalidFilterError('NOT (!) clause in filter cannot be multiple')
  188. current_node.append(evaluate_match(search_filter[start_pos:end_pos], schema, auto_escape, auto_encode, validator, check_names))
  189. start_pos = None
  190. state = SEARCH_OPEN_OR_CLOSE
  191. elif (state == SEARCH_MATCH_OR_CLOSE or state == SEARCH_MATCH_OR_CONTROL) and c not in '()':
  192. skip_white_space = False
  193. if not start_pos:
  194. start_pos = pos
  195. state = SEARCH_MATCH_OR_CLOSE
  196. else:
  197. raise LDAPInvalidFilterError('malformed filter')
  198. if len(root.elements) != 1:
  199. raise LDAPInvalidFilterError('missing boolean operator in filter')
  200. return root
  201. else:
  202. raise LDAPInvalidFilterError('invalid filter')
  203. def compile_filter(filter_node):
  204. """Builds ASN1 structure for filter, converts from filter LDAP escaping to bytes"""
  205. compiled_filter = Filter()
  206. if filter_node.tag == AND:
  207. boolean_filter = And()
  208. pos = 0
  209. for element in filter_node.elements:
  210. boolean_filter[pos] = compile_filter(element)
  211. pos += 1
  212. compiled_filter['and'] = boolean_filter
  213. elif filter_node.tag == OR:
  214. boolean_filter = Or()
  215. pos = 0
  216. for element in filter_node.elements:
  217. boolean_filter[pos] = compile_filter(element)
  218. pos += 1
  219. compiled_filter['or'] = boolean_filter
  220. elif filter_node.tag == NOT:
  221. boolean_filter = Not()
  222. boolean_filter['innerNotFilter'] = compile_filter(filter_node.elements[0])
  223. compiled_filter.setComponentByName('notFilter', boolean_filter, verifyConstraints=False) # do not verify constraints because of hack for recursive filters in rfc4511
  224. elif filter_node.tag == MATCH_APPROX:
  225. matching_filter = ApproxMatch()
  226. matching_filter['attributeDesc'] = AttributeDescription(filter_node.assertion['attr'])
  227. matching_filter['assertionValue'] = AssertionValue(prepare_filter_for_sending(filter_node.assertion['value']))
  228. compiled_filter['approxMatch'] = matching_filter
  229. elif filter_node.tag == MATCH_GREATER_OR_EQUAL:
  230. matching_filter = GreaterOrEqual()
  231. matching_filter['attributeDesc'] = AttributeDescription(filter_node.assertion['attr'])
  232. matching_filter['assertionValue'] = AssertionValue(prepare_filter_for_sending(filter_node.assertion['value']))
  233. compiled_filter['greaterOrEqual'] = matching_filter
  234. elif filter_node.tag == MATCH_LESS_OR_EQUAL:
  235. matching_filter = LessOrEqual()
  236. matching_filter['attributeDesc'] = AttributeDescription(filter_node.assertion['attr'])
  237. matching_filter['assertionValue'] = AssertionValue(prepare_filter_for_sending(filter_node.assertion['value']))
  238. compiled_filter['lessOrEqual'] = matching_filter
  239. elif filter_node.tag == MATCH_EXTENSIBLE:
  240. matching_filter = ExtensibleMatch()
  241. if filter_node.assertion['matchingRule']:
  242. matching_filter['matchingRule'] = MatchingRule(filter_node.assertion['matchingRule'])
  243. if filter_node.assertion['attr']:
  244. matching_filter['type'] = Type(filter_node.assertion['attr'])
  245. matching_filter['matchValue'] = MatchValue(prepare_filter_for_sending(filter_node.assertion['value']))
  246. matching_filter['dnAttributes'] = DnAttributes(filter_node.assertion['dnAttributes'])
  247. compiled_filter['extensibleMatch'] = matching_filter
  248. elif filter_node.tag == MATCH_PRESENT:
  249. matching_filter = Present(AttributeDescription(filter_node.assertion['attr']))
  250. compiled_filter['present'] = matching_filter
  251. elif filter_node.tag == MATCH_SUBSTRING:
  252. matching_filter = SubstringFilter()
  253. matching_filter['type'] = AttributeDescription(filter_node.assertion['attr'])
  254. substrings = Substrings()
  255. pos = 0
  256. if 'initial' in filter_node.assertion and filter_node.assertion['initial']:
  257. substrings[pos] = Substring().setComponentByName('initial', Initial(prepare_filter_for_sending(filter_node.assertion['initial'])))
  258. pos += 1
  259. if 'any' in filter_node.assertion and filter_node.assertion['any']:
  260. for substring in filter_node.assertion['any']:
  261. substrings[pos] = Substring().setComponentByName('any', Any(prepare_filter_for_sending(substring)))
  262. pos += 1
  263. if 'final' in filter_node.assertion and filter_node.assertion['final']:
  264. substrings[pos] = Substring().setComponentByName('final', Final(prepare_filter_for_sending(filter_node.assertion['final'])))
  265. matching_filter['substrings'] = substrings
  266. compiled_filter['substringFilter'] = matching_filter
  267. elif filter_node.tag == MATCH_EQUAL:
  268. matching_filter = EqualityMatch()
  269. matching_filter['attributeDesc'] = AttributeDescription(filter_node.assertion['attr'])
  270. matching_filter['assertionValue'] = AssertionValue(prepare_filter_for_sending(filter_node.assertion['value']))
  271. compiled_filter.setComponentByName('equalityMatch', matching_filter)
  272. else:
  273. raise LDAPInvalidFilterError('unknown filter node tag')
  274. return compiled_filter
  275. def build_attribute_selection(attribute_list, schema):
  276. conf_attributes_excluded_from_check = [v.lower() for v in get_config_parameter('ATTRIBUTES_EXCLUDED_FROM_CHECK')]
  277. attribute_selection = AttributeSelection()
  278. for index, attribute in enumerate(attribute_list):
  279. if schema and schema.attribute_types:
  280. if ';' in attribute: # exclude tags from validation
  281. if not attribute[0:attribute.index(';')] in schema.attribute_types and attribute.lower() not in conf_attributes_excluded_from_check:
  282. raise LDAPAttributeError('invalid attribute type in attribute list: ' + attribute)
  283. else:
  284. if attribute not in schema.attribute_types and attribute.lower() not in conf_attributes_excluded_from_check:
  285. raise LDAPAttributeError('invalid attribute type in attribute list: ' + attribute)
  286. attribute_selection[index] = Selector(attribute)
  287. return attribute_selection
  288. def search_operation(search_base,
  289. search_filter,
  290. search_scope,
  291. dereference_aliases,
  292. attributes,
  293. size_limit,
  294. time_limit,
  295. types_only,
  296. auto_escape,
  297. auto_encode,
  298. schema=None,
  299. validator=None,
  300. check_names=False):
  301. # SearchRequest ::= [APPLICATION 3] SEQUENCE {
  302. # baseObject LDAPDN,
  303. # scope ENUMERATED {
  304. # baseObject (0),
  305. # singleLevel (1),
  306. # wholeSubtree (2),
  307. # ... },
  308. # derefAliases ENUMERATED {
  309. # neverDerefAliases (0),
  310. # derefInSearching (1),
  311. # derefFindingBaseObj (2),
  312. # derefAlways (3) },
  313. # sizeLimit INTEGER (0 .. maxInt),
  314. # timeLimit INTEGER (0 .. maxInt),
  315. # typesOnly BOOLEAN,
  316. # filter Filter,
  317. # attributes AttributeSelection }
  318. request = SearchRequest()
  319. request['baseObject'] = LDAPDN(search_base)
  320. if search_scope == BASE or search_scope == 0:
  321. request['scope'] = Scope('baseObject')
  322. elif search_scope == LEVEL or search_scope == 1:
  323. request['scope'] = Scope('singleLevel')
  324. elif search_scope == SUBTREE or search_scope == 2:
  325. request['scope'] = Scope('wholeSubtree')
  326. else:
  327. raise LDAPInvalidScopeError('invalid scope type')
  328. if dereference_aliases == DEREF_NEVER or dereference_aliases == 0:
  329. request['derefAliases'] = DerefAliases('neverDerefAliases')
  330. elif dereference_aliases == DEREF_SEARCH or dereference_aliases == 1:
  331. request['derefAliases'] = DerefAliases('derefInSearching')
  332. elif dereference_aliases == DEREF_BASE or dereference_aliases == 2:
  333. request['derefAliases'] = DerefAliases('derefFindingBaseObj')
  334. elif dereference_aliases == DEREF_ALWAYS or dereference_aliases == 3:
  335. request['derefAliases'] = DerefAliases('derefAlways')
  336. else:
  337. raise LDAPInvalidDereferenceAliasesError('invalid dereference aliases type')
  338. request['sizeLimit'] = Integer0ToMax(size_limit)
  339. request['timeLimit'] = Integer0ToMax(time_limit)
  340. request['typesOnly'] = TypesOnly(True) if types_only else TypesOnly(False)
  341. request['filter'] = compile_filter(parse_filter(search_filter, schema, auto_escape, auto_encode, validator, check_names).elements[0]) # parse the searchFilter string and compile it starting from the root node
  342. if not isinstance(attributes, SEQUENCE_TYPES):
  343. attributes = [NO_ATTRIBUTES]
  344. request['attributes'] = build_attribute_selection(attributes, schema)
  345. return request
  346. def decode_vals(vals):
  347. return [str(val) for val in vals if val] if vals else None
  348. def decode_vals_fast(vals):
  349. try:
  350. return [to_unicode(val[3], from_server=True) for val in vals if val] if vals else None
  351. except UnicodeDecodeError:
  352. return [val[3] for val in vals if val] if vals else None
  353. def attributes_to_dict(attribute_list):
  354. conf_case_insensitive_attributes = get_config_parameter('CASE_INSENSITIVE_ATTRIBUTE_NAMES')
  355. attributes = CaseInsensitiveDict() if conf_case_insensitive_attributes else dict()
  356. for attribute in attribute_list:
  357. attributes[str(attribute['type'])] = decode_vals(attribute['vals'])
  358. return attributes
  359. def attributes_to_dict_fast(attribute_list):
  360. conf_case_insensitive_attributes = get_config_parameter('CASE_INSENSITIVE_ATTRIBUTE_NAMES')
  361. attributes = CaseInsensitiveDict() if conf_case_insensitive_attributes else dict()
  362. for attribute in attribute_list:
  363. attributes[to_unicode(attribute[3][0][3], from_server=True)] = decode_vals_fast(attribute[3][1][3])
  364. return attributes
  365. def decode_raw_vals(vals):
  366. return [bytes(val) for val in vals] if vals else None
  367. def decode_raw_vals_fast(vals):
  368. return [bytes(val[3]) for val in vals] if vals else None
  369. def raw_attributes_to_dict(attribute_list):
  370. conf_case_insensitive_attributes = get_config_parameter('CASE_INSENSITIVE_ATTRIBUTE_NAMES')
  371. attributes = CaseInsensitiveDict() if conf_case_insensitive_attributes else dict()
  372. for attribute in attribute_list:
  373. attributes[str(attribute['type'])] = decode_raw_vals(attribute['vals'])
  374. return attributes
  375. def raw_attributes_to_dict_fast(attribute_list):
  376. conf_case_insensitive_attributes = get_config_parameter('CASE_INSENSITIVE_ATTRIBUTE_NAMES')
  377. attributes = CaseInsensitiveDict() if conf_case_insensitive_attributes else dict()
  378. for attribute in attribute_list:
  379. attributes[to_unicode(attribute[3][0][3], from_server=True)] = decode_raw_vals_fast(attribute[3][1][3])
  380. return attributes
  381. def checked_attributes_to_dict(attribute_list, schema=None, custom_formatter=None):
  382. conf_case_insensitive_attributes = get_config_parameter('CASE_INSENSITIVE_ATTRIBUTE_NAMES')
  383. checked_attributes = CaseInsensitiveDict() if conf_case_insensitive_attributes else dict()
  384. for attribute in attribute_list:
  385. name = str(attribute['type'])
  386. checked_attributes[name] = format_attribute_values(schema, name, decode_raw_vals(attribute['vals']) or [], custom_formatter)
  387. return checked_attributes
  388. def checked_attributes_to_dict_fast(attribute_list, schema=None, custom_formatter=None):
  389. conf_case_insensitive_attributes = get_config_parameter('CASE_INSENSITIVE_ATTRIBUTE_NAMES')
  390. checked_attributes = CaseInsensitiveDict() if conf_case_insensitive_attributes else dict()
  391. for attribute in attribute_list:
  392. name = to_unicode(attribute[3][0][3], from_server=True)
  393. checked_attributes[name] = format_attribute_values(schema, name, decode_raw_vals_fast(attribute[3][1][3]) or [], custom_formatter)
  394. return checked_attributes
  395. def matching_rule_assertion_to_string(matching_rule_assertion):
  396. return str(matching_rule_assertion)
  397. def filter_to_string(filter_object):
  398. filter_type = filter_object.getName()
  399. filter_string = '('
  400. if filter_type == 'and':
  401. filter_string += '&'
  402. for f in filter_object['and']:
  403. filter_string += filter_to_string(f)
  404. elif filter_type == 'or':
  405. filter_string += '|'
  406. for f in filter_object['or']:
  407. filter_string += filter_to_string(f)
  408. elif filter_type == 'notFilter':
  409. filter_string += '!' + filter_to_string(filter_object['notFilter']['innerNotFilter'])
  410. elif filter_type == 'equalityMatch':
  411. ava = ava_to_dict(filter_object['equalityMatch'])
  412. filter_string += ava['attribute'] + '=' + ava['value']
  413. elif filter_type == 'substringFilter':
  414. attribute = filter_object['substringFilter']['type']
  415. filter_string += str(attribute) + '='
  416. for substring in filter_object['substringFilter']['substrings']:
  417. component = substring.getName()
  418. if substring[component] is not None and substring[component].hasValue():
  419. if component == 'initial':
  420. filter_string += str(substring['initial']) + '*'
  421. elif component == 'any':
  422. filter_string += str(substring['any']) if filter_string.endswith('*') else '*' + str(substring['any'])
  423. filter_string += '*'
  424. elif component == 'final':
  425. filter_string += '*' + str(substring['final'])
  426. elif filter_type == 'greaterOrEqual':
  427. ava = ava_to_dict(filter_object['greaterOrEqual'])
  428. filter_string += ava['attribute'] + '>=' + ava['value']
  429. elif filter_type == 'lessOrEqual':
  430. ava = ava_to_dict(filter_object['lessOrEqual'])
  431. filter_string += ava['attribute'] + '<=' + ava['value']
  432. elif filter_type == 'present':
  433. filter_string += str(filter_object['present']) + '=*'
  434. elif filter_type == 'approxMatch':
  435. ava = ava_to_dict(filter_object['approxMatch'])
  436. filter_string += ava['attribute'] + '~=' + ava['value']
  437. elif filter_type == 'extensibleMatch':
  438. filter_string += matching_rule_assertion_to_string(filter_object['extensibleMatch'])
  439. else:
  440. raise LDAPInvalidFilterError('error converting filter to string')
  441. filter_string += ')'
  442. if str == bytes: # Python2, forces conversion to Unicode
  443. filter_string = to_unicode(filter_string)
  444. return filter_string
  445. def search_request_to_dict(request):
  446. return {'base': str(request['baseObject']),
  447. 'scope': int(request['scope']),
  448. 'dereferenceAlias': int(request['derefAliases']),
  449. 'sizeLimit': int(request['sizeLimit']),
  450. 'timeLimit': int(request['timeLimit']),
  451. 'typesOnly': bool(request['typesOnly']),
  452. 'filter': filter_to_string(request['filter']),
  453. 'attributes': attributes_to_list(request['attributes'])}
  454. def search_result_entry_response_to_dict(response, schema, custom_formatter, check_names):
  455. entry = dict()
  456. # entry['dn'] = str(response['object'])
  457. if response['object']:
  458. entry['raw_dn'] = to_raw(response['object'])
  459. if isinstance(response['object'], STRING_TYPES): # mock strategies return string not a PyAsn1 object
  460. entry['dn'] = to_unicode(response['object'])
  461. else:
  462. entry['dn'] = to_unicode(bytes(response['object']), from_server=True)
  463. else:
  464. entry['raw_dn'] = b''
  465. entry['dn'] = ''
  466. entry['raw_attributes'] = raw_attributes_to_dict(response['attributes'])
  467. if check_names:
  468. entry['attributes'] = checked_attributes_to_dict(response['attributes'], schema, custom_formatter)
  469. else:
  470. entry['attributes'] = attributes_to_dict(response['attributes'])
  471. return entry
  472. def search_result_done_response_to_dict(response):
  473. result = {'result': int(response['resultCode']),
  474. 'description': ResultCode().getNamedValues().getName(response['resultCode']),
  475. 'message': str(response['diagnosticMessage']),
  476. 'dn': str(response['matchedDN']),
  477. 'referrals': referrals_to_list(response['referral'])}
  478. if 'controls' in response: # used for returning controls in Mock strategies
  479. result['controls'] = dict()
  480. for control in response['controls']:
  481. result['controls'][control[0]] = control[1]
  482. return result
  483. def search_result_reference_response_to_dict(response):
  484. return {'uri': search_refs_to_list(response)}
  485. def search_result_entry_response_to_dict_fast(response, schema, custom_formatter, check_names):
  486. entry_dict = dict()
  487. entry_dict['raw_dn'] = response[0][3]
  488. entry_dict['dn'] = to_unicode(response[0][3], from_server=True)
  489. entry_dict['raw_attributes'] = raw_attributes_to_dict_fast(response[1][3]) # attributes
  490. if check_names:
  491. entry_dict['attributes'] = checked_attributes_to_dict_fast(response[1][3], schema, custom_formatter) # attributes
  492. else:
  493. entry_dict['attributes'] = attributes_to_dict_fast(response[1][3]) # attributes
  494. return entry_dict
  495. def search_result_reference_response_to_dict_fast(response):
  496. return {'uri': search_refs_to_list_fast([r[3] for r in response])}